Skip to content

Commit e90a4c9

Browse files
authored
Merge pull request #263 from hypercerts-org/feat/refactor_supabase_services
Refactor supabase services
2 parents 54d79c8 + a4b32b8 commit e90a4c9

File tree

243 files changed

+21842
-5805
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

243 files changed

+21842
-5805
lines changed

README.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ The API implements a fallback to the first available RPC. You can set the RPCs i
2828

2929
### Supabase
3030

31-
* Install Docker
32-
* `git submodule init`
33-
* `git submodule update --remote`
34-
* `pnpm supabase:start:all`
31+
- Install Docker
32+
- `git submodule init`
33+
- `git submodule update --remote`
34+
- `pnpm supabase:start:all`
3535

3636
This will spin up 2 Supabase instances in Docker, one for the indexer service (caching) and one for the data service (static data) which are both exposed by the API.
3737

@@ -43,21 +43,21 @@ From both instances, you can get their respective keys and add them to the env v
4343

4444
This will run a live production instance by running `swc` to compile the code and `nodemon` to restart the server on changes.
4545

46-
You can then find the API at `localhost:4000/spec` (Swagger instance) and the GraphQL at `localhost:4000/v1/graphql`
46+
You can then find the API at `localhost:4000/spec` (Swagger instance) and the GraphQL at `localhost:4000/v2/graphql`
4747

4848
## Deployments
4949

5050
Production: `https://api.hypercerts.org/`
5151
Staging: `https://staging-api.hypercerts.org`
5252

5353
`/spec` - Swagger instance documenting the API and exposing a playground to experiment with the endpoints
54-
`/v1/graphql` - GraphQL API to access hypercerts data like claims, fractions, attestations, allow lists
54+
`/v2/graphql` - GraphQL API to access hypercerts data like claims, fractions, attestations, allow lists
5555

5656
## Scripts
5757

5858
- `dev`: Starts the development server using `nodemon`, which will automatically restart the server whenever you save a file that the server uses.
5959
- `build`: Denerates the OpenAPI specification and routes using `tsoa`, and then compiles the TypeScript code into JavaScript using `swc`. The compiled code is output to the `dist` directory.
60-
- `start`: Starts the application in production mode.
60+
- `start`: Starts the application in production mode.
6161
- `lint`: Runs `eslint` on the codebase to check for linting errors.
6262
- `test`: Runs tests using `vitest`
6363

@@ -86,38 +86,38 @@ The API also provides an upload and validation endpoint for hypercert and allow
8686
graph TB
8787
Client[Client Applications]
8888
API[Hypercerts API :4000]
89-
89+
9090
subgraph "API Endpoints"
9191
Swagger["/spec\nSwagger Documentation"]
92-
GraphQL["/v1/graphql\nGraphQL Endpoint"]
92+
GraphQL["/v2/graphql\nGraphQL Endpoint"]
9393
Upload["Upload & Validation\nEndpoints"]
9494
end
95-
95+
9696
subgraph "Data Services"
9797
Static[("Static Data Service\n(Supabase DB)\n- User Data\n- Collections\n- Signed Orders")]
9898
Indexer[("Indexer Service\n(Supabase DB)\n- On-chain Data\n- IPFS Data")]
9999
end
100-
100+
101101
subgraph "External Services"
102102
IPFS[(IPFS\nMetadata Storage)]
103103
Blockchain[(Blockchain\nSupported Chains)]
104104
EAS[(EAS\nAttestations)]
105105
end
106-
106+
107107
Client --> API
108108
API --> Swagger
109109
API --> GraphQL
110110
API --> Upload
111-
111+
112112
GraphQL --> Static
113113
GraphQL --> Indexer
114114
Upload --> IPFS
115-
115+
116116
Indexer --> Blockchain
117117
Indexer --> IPFS
118118
Indexer --> EAS
119-
119+
120120
class Swagger,GraphQL,Upload apiEndpoint;
121121
class Static,Indexer database;
122122
class IPFS,Blockchain,EAS external;
123-
```
123+
```

docs/DEVELOPMENT.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Development Guide: Implementing a New Entity
2+
3+
This guide explains how to implement a new entity in the Hypercerts API, from type definition to resolver implementation.
4+
5+
## Overview
6+
7+
The Hypercerts API uses a modular architecture where each entity follows a consistent pattern:
8+
9+
1. Type Definition
10+
2. Query Arguments
11+
3. Entity Service
12+
4. Resolver
13+
14+
## Step-by-Step Implementation
15+
16+
### 1. Define Entity Types
17+
18+
Create a new file in `src/graphql/schemas/typeDefs/` for your entity types:
19+
20+
```typescript
21+
// src/graphql/schemas/typeDefs/yourEntityTypeDefs.ts
22+
import { Field, ObjectType } from "type-graphql";
23+
import { BaseEntity } from "./baseTypes.js";
24+
25+
@ObjectType()
26+
export class YourEntity extends BaseEntity {
27+
@Field(() => String)
28+
name: string;
29+
30+
@Field(() => String, { nullable: true })
31+
description?: string;
32+
33+
// Add other fields as needed
34+
}
35+
36+
@ObjectType()
37+
export class GetYourEntitiesResponse {
38+
@Field(() => [YourEntity])
39+
data: YourEntity[];
40+
41+
@Field(() => Int)
42+
count: number;
43+
}
44+
```
45+
46+
### 2. Define Query Arguments
47+
48+
Create a new file in `src/graphql/schemas/args/` for your query arguments:
49+
50+
```typescript
51+
// src/graphql/schemas/args/yourEntityArgs.ts
52+
import { ArgsType } from "type-graphql";
53+
import { createEntityArgs } from "../../../lib/graphql/createEntityArgs.js";
54+
import { EntityTypeDefs } from "../typeDefs/typeDefs.js";
55+
56+
// Define your entity fields
57+
const fields = {
58+
name: "string",
59+
description: "string",
60+
// Add other fields as needed
61+
} as const;
62+
63+
// Create query arguments
64+
export const { WhereInput, SortOptions } = createEntityArgs(
65+
"YourEntity" as EntityTypeDefs,
66+
fields,
67+
);
68+
69+
@ArgsType()
70+
export class GetYourEntitiesArgs {
71+
first?: number;
72+
offset?: number;
73+
where?: typeof WhereInput;
74+
sortBy?: typeof SortOptions;
75+
}
76+
```
77+
78+
### 3. Create Entity Service
79+
80+
Create a new file in `src/services/database/entities/` for your entity service:
81+
82+
```typescript
83+
// src/services/database/entities/YourEntityService.ts
84+
import { injectable } from "tsyringe";
85+
import { createEntityService } from "./EntityServiceFactory.js";
86+
import { GetYourEntitiesArgs } from "../../../graphql/schemas/args/yourEntityArgs.js";
87+
import { YourEntity } from "../../../graphql/schemas/typeDefs/yourEntityTypeDefs.js";
88+
89+
@injectable()
90+
export class YourEntityService {
91+
private service = createEntityService<YourEntity, GetYourEntitiesArgs>(
92+
"your_entity_table",
93+
{
94+
// Add any custom query modifiers if needed
95+
},
96+
);
97+
98+
async getYourEntities(args: GetYourEntitiesArgs) {
99+
return this.service.getMany(args);
100+
}
101+
102+
async getYourEntity(args: GetYourEntitiesArgs) {
103+
return this.service.getSingle(args);
104+
}
105+
}
106+
```
107+
108+
### 4. Implement Resolver
109+
110+
Create a new file in `src/graphql/schemas/resolvers/` for your resolver:
111+
112+
```typescript
113+
// src/graphql/schemas/resolvers/yourEntityResolver.ts
114+
import { inject, injectable } from "tsyringe";
115+
import { Args, Query, Resolver } from "type-graphql";
116+
import { YourEntityService } from "../../../services/database/entities/YourEntityService.js";
117+
import { GetYourEntitiesArgs } from "../args/yourEntityArgs.js";
118+
import {
119+
GetYourEntitiesResponse,
120+
YourEntity,
121+
} from "../typeDefs/yourEntityTypeDefs.js";
122+
123+
@injectable()
124+
@Resolver(() => YourEntity)
125+
class YourEntityResolver {
126+
constructor(
127+
@inject(YourEntityService)
128+
private yourEntityService: YourEntityService,
129+
) {}
130+
131+
@Query(() => GetYourEntitiesResponse)
132+
async yourEntities(@Args() args: GetYourEntitiesArgs) {
133+
return this.yourEntityService.getYourEntities(args);
134+
}
135+
}
136+
```
137+
138+
### 5. Register the Resolver
139+
140+
Add your resolver to the list of resolvers in `src/graphql/schemas/resolvers/index.ts`:
141+
142+
```typescript
143+
export * from "./yourEntityResolver.js";
144+
```
145+
146+
## Best Practices
147+
148+
1. **Type Safety**: Always use TypeScript's type system to ensure type safety across your implementation.
149+
2. **Consistent Naming**: Follow the existing naming conventions in the codebase.
150+
3. **Error Handling**: Implement proper error handling in your service and resolver methods.
151+
4. **Testing**: Write unit tests for your new entity implementation.
152+
5. **Documentation**: Add JSDoc comments to document your types, methods, and classes.
153+
154+
## Example Implementation
155+
156+
For a complete example, you can look at the implementation of existing entities like `Contract`, `Metadata`, or `AttestationSchema` in the codebase.
157+
158+
## Common Pitfalls
159+
160+
1. **Type Registration**: Ensure all your types are properly registered in the GraphQL schema.
161+
2. **Dependency Injection**: Use the `@injectable()` and `@inject()` decorators correctly.
162+
3. **Query Arguments**: Make sure your query arguments match the expected structure.
163+
4. **Database Schema**: Ensure your database table matches the entity structure.
164+
165+
## Testing Your Implementation
166+
167+
1. Start the development server: `pnpm dev`
168+
2. Access the GraphQL playground at `http://localhost:4000/v2/graphql`
169+
3. Test your queries and mutations
170+
4. Run the test suite: `pnpm test`
171+
172+
## Additional Resources
173+
174+
- [TypeGraphQL Documentation](https://typegraphql.com/)
175+
- [Kysely Documentation](https://kysely.dev/docs/intro)
176+
- [Supabase Documentation](https://supabase.com/docs)

eslint.config.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,14 @@ export default tseslint.config(
99
"@typescript-eslint/no-extraneous-class": "off",
1010
},
1111
},
12+
{
13+
files: ["**/*.test.ts"],
14+
rules: {
15+
"@typescript-eslint/no-explicit-any": "off",
16+
"@typescript-eslint/no-unused-vars": [
17+
"error",
18+
{ argsIgnorePattern: "^_" },
19+
],
20+
},
21+
},
1222
);

package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "module",
66
"scripts": {
77
"dev": "nodemon",
8-
"build": "rimraf dist && tsoa spec-and-routes && swc src --out-dir dist --copy-files",
8+
"build": "rimraf dist && tsoa spec-and-routes && tsc && swc src --out-dir dist --copy-files",
99
"start": "node -r dotenv/config dist/src/index.js",
1010
"integration": "concurrently -c \"green,blue\" --names \"CACHE,DATA\" \"pnpm --dir ./lib/hypercerts-indexer run dev\" \"pnpm run dev\"",
1111
"supabase:reset:all": "concurrently -c \"blue,green\" --names \"DATA,CACHE\" \"npm run supabase:reset:data\" \"npm run supabase:reset:cache\"",
@@ -26,6 +26,7 @@
2626
"commitlint": "commitlint --config commitlintrc.ts --edit"
2727
},
2828
"dependencies": {
29+
"@faker-js/faker": "^9.6.0",
2930
"@graphql-tools/merge": "^9.0.19",
3031
"@graphql-yoga/plugin-response-cache": "^3.13.0",
3132
"@hypercerts-org/contracts": "2.0.0-alpha.12",
@@ -53,6 +54,7 @@
5354
"@web3-storage/w3up-client": "^16.0.0",
5455
"axios": "^1.6.5",
5556
"cors": "^2.8.5",
57+
"date-fns": "^4.1.0",
5658
"ethers": "^6.12.2",
5759
"express": "^4.19.2",
5860
"file-type": "^19.6.0",
@@ -99,6 +101,9 @@
99101
"@sentry/types": "^8.2.1",
100102
"@swc/cli": "^0.3.12",
101103
"@swc/core": "^1.4.15",
104+
"@swc/helpers": "^0.5.15",
105+
"@swc/jest": "^0.2.37",
106+
"@types/better-sqlite3": "^7.6.12",
102107
"@types/body-parser": "^1.19.5",
103108
"@types/mime-types": "^2.1.4",
104109
"@types/multer": "^1.4.12",
@@ -107,6 +112,7 @@
107112
"@types/sinon": "^17.0.2",
108113
"@types/swagger-ui-express": "^4.1.6",
109114
"@vitest/coverage-v8": "^2.1.8",
115+
"better-sqlite3": "^11.8.1",
110116
"chai": "^5.0.0",
111117
"chai-assertions-count": "^1.0.2",
112118
"concurrently": "^8.2.2",
@@ -119,6 +125,7 @@
119125
"multiformats": "^13.0.0",
120126
"node-mocks-http": "^1.14.1",
121127
"nodemon": "^3.0.3",
128+
"pg-mem": "^3.0.5",
122129
"prettier": "3.3.2",
123130
"rimraf": "^5.0.5",
124131
"sinon": "^17.0.1",
@@ -129,6 +136,7 @@
129136
"typedoc": "^0.26.5",
130137
"typescript": "5.5.3",
131138
"typescript-eslint": "^7.7.0",
139+
"unplugin-swc": "^1.5.1",
132140
"vite-tsconfig-paths": "^5.1.4",
133141
"vitest": "^2.1.8",
134142
"vitest-mock-extended": "^2.0.2"

0 commit comments

Comments
 (0)