1 | # TypeScript MongoDB Typings
|
2 |
|
3 | ## Overview
|
4 |
|
5 | This package generates TypeScript types for MongoDB server-side developers.
|
6 |
|
7 | It uses GraphQL directives to declare the types you want to generate and use in your MongoDB.
|
8 |
|
9 | It take a declaration like this:
|
10 |
|
11 | ```graphql
|
12 | type User @entity {
|
13 | id: String @id
|
14 | username: String! @column
|
15 | email: @column
|
16 | }
|
17 | ```
|
18 |
|
19 | And generates a TypeScript interface like this:
|
20 |
|
21 | ```typescript
|
22 | import { ObjectID } from 'mongodb';
|
23 |
|
24 | export interface UserDbObject {
|
25 | _id: ObjectID;
|
26 | username: string;
|
27 | email?: string | null;
|
28 | }
|
29 | ```
|
30 |
|
31 | So you can use it when you read/write to your database, and expect a specific structure.
|
32 |
|
33 | ## How to use?
|
34 |
|
35 | To use this package, install is using Yarn into your project, as a dependency:
|
36 |
|
37 | ```
|
38 | yarn add graphql-codegen-typescript-mongodb-template
|
39 | ```
|
40 |
|
41 | Then, you need to add the directives declaration to your GraphQL Schema definition:
|
42 |
|
43 | ```typescript
|
44 | import { makeExecutableSchema } from 'graphql-tools';
|
45 | import { DIRECTIVES } from 'graphql-codegen-typescript-mongodb-template';
|
46 |
|
47 | const schema = makeExecutableSchema({
|
48 | typeDefs: [
|
49 | DIRECTIVES
|
50 | // the rest of your GraphQL types
|
51 | ],
|
52 | resolvers
|
53 | });
|
54 | ```
|
55 |
|
56 | Now, use this template with the GraphQL code generator:
|
57 |
|
58 | ```
|
59 | gql-gen --template graphql-codegen-typescript-mongodb-template --schema ./src/my-schema.js
|
60 | ```
|
61 |
|
62 | > IMPORTANT: Because of GraphQL behavior, you have to use --schema and provide a JavaScript export, otherwise you will lose your GraphQL Directives usage
|
63 |
|
64 | Now, add the directives to your GraphQL definitions, and generate your MongoDB models file.
|
65 |
|
66 | ## Directives
|
67 |
|
68 | ### `@entity(embedded: Boolean, additionalFields: [AdditionalEntityFields])` (on `OBJECT`)
|
69 |
|
70 | Use this directive to declare that a specific GraphQL type should have a generated MongoDB models.
|
71 |
|
72 | - `embedded: Boolean` - specify this to declare that this entity turns into an object inside another entity. For example, if you want your structure to be `{ _id: string, username: string, profile: { name: string }}`, then your GraphQL type `Profile` should be an embedded type.
|
73 | - `additionalFields: [AdditionalEntityFields]` - specify any additional fields that you with to add to your MongoDB object, and are not part of your public GraphQL schema.
|
74 |
|
75 | ```graphql
|
76 | type User @entity(additionalFields: [
|
77 | { path: "services.login.token", type: "string" }
|
78 | ]) {
|
79 | id: String @id
|
80 | email: @column
|
81 | }
|
82 | ```
|
83 |
|
84 | ### `@column(name: string, overrideType: String, overrideIsArray: Boolean)` (on `FIELD_DEFINITION`)
|
85 |
|
86 | Use this directive to declare that a specific GraphQL field should be part of your generated MongoDB type.
|
87 |
|
88 | - `overrideType: String` - use this to override the type of the field, for example, if you persist dates as `Date` but expose them as `String`.
|
89 | - `overrideIsArray: Boolean` - specify `true`/`false` to override the generated result and force it to generate an array type (or not).
|
90 | - `name: String` - you can specify the name of the field as you wish to have it on your DB - it could be also a path (`a.b.c`). This is a shortcut for `@map` directive.
|
91 |
|
92 | > Note that if your property is an embedded entity, you should use `@embedded` instead.
|
93 |
|
94 | ### `@id` (on `FIELD_DEFINITION`)
|
95 |
|
96 | Use this directive on your field that turns into your MongoDB `_id`. Usually it's your `id` field of the GraphQL `type`.
|
97 |
|
98 | ### `@link` (on `FIELD_DEFINITION`)
|
99 |
|
100 | Use this directive to declare that a specific field is a link to another type in another table. This will use the `ObjectID` type in your generated result.
|
101 |
|
102 | ### `@embedded` (on `FIELD_DEFINITION`)
|
103 |
|
104 | Use this directive to declare that a specific field is integrated into the parent entity, and should be an object inside it.
|
105 |
|
106 | ### `@map(path: String)` (on `FIELD_DEFINITION`)
|
107 |
|
108 | Use this directive to override the `path` or the name of the field. It's useful for creating a more complex object structure on the database.
|
109 |
|
110 | For example, if you wish to expose a field as `username` on your schema, but persist it in `credentials.username` on your DB.
|
111 |
|
112 | You can either specify the name of the field, or a `path` to it to generate an object.
|
113 |
|
114 | ```graphql
|
115 | type User @entity {
|
116 | username: String @column @map(path: "credentials.username")
|
117 | }
|
118 | ```
|
119 |
|
120 | Will output:
|
121 |
|
122 | ```typescript
|
123 | export interface UserDbObject {
|
124 | credentials: {
|
125 | username: string;
|
126 | };
|
127 | }
|
128 | ```
|
129 |
|
130 | ### `@abstractEntity(discriminatorField: String!)` (on `INTERFACE`)
|
131 |
|
132 | Use this directive on your GraphQL `interface`s to indicate that this interface is a common base for other database types.
|
133 |
|
134 | Specify `discriminatorField` to tell the generator what is the name of the field you are using on your database to identify how which object is implementing the interface.
|
135 |
|
136 | For example:
|
137 |
|
138 | ```graphql
|
139 | interface BaseNotification @abstractEntity(discriminatorField: "notificationType") {
|
140 | id: ID! @id
|
141 | createdAt: String! @column(overrideType: "Date")
|
142 | }
|
143 |
|
144 | type TextNotification implements BaseNotification @entity {
|
145 | id: ID!
|
146 | createdAt: String!
|
147 | content: String! @column
|
148 | }
|
149 | ```
|
150 |
|
151 | This way, you will get:
|
152 |
|
153 | ```typescript
|
154 | export interface BaseNotificationDbInterface {
|
155 | notificationType: string;
|
156 | _id: ObjectID;
|
157 | createdAt: Date;
|
158 | }
|
159 |
|
160 | export interface TextNotificationDbObject extends BaseNotificationDbInterface {
|
161 | content: string;
|
162 | }
|
163 | ```
|
164 |
|
165 | ### `@union(discriminatorField: String)` (on `UNION`)
|
166 |
|
167 | This directive is similar to `@abstractEntity`, but for unions (that not necessarily have common fields).
|
168 |
|
169 | Specify `discriminatorField` to tell the generator what is the name of the field you are using on your database to identify how which object is implementing the interface.
|
170 |
|
171 | ```graphql
|
172 | type A @entity {
|
173 | fieldA: String @column
|
174 | }
|
175 |
|
176 | type B @entity {
|
177 | fieldB: String @column
|
178 | }
|
179 |
|
180 | union PossibleType = A | B @union(discriminatorField: "entityType")
|
181 | ```
|
182 |
|
183 | You will get:
|
184 |
|
185 | ```typescript
|
186 | export interface ADbObject {
|
187 | fieldA: string;
|
188 | }
|
189 |
|
190 | export interface BDbObject {
|
191 | fieldB: string;
|
192 | }
|
193 |
|
194 | export type PossibleType = { entityType: string } & (ADbObject | BDbObject);
|
195 | ```
|
196 |
|
197 | ## Simple Example
|
198 |
|
199 | For example, if you have a `User` (that matches to `users` table), `Profile` (which is part of your `User`) and `Friends` which is an array of `ObjectID` inside your user, use the following schema:
|
200 |
|
201 | ```
|
202 | type User @entity {
|
203 | id: String! @id
|
204 | username: String! @column
|
205 | email: String! @column
|
206 | profile: Profile! @embedded
|
207 | friendsCount: Int! # this field won't get a generated MongoDB field
|
208 | friends: [User]! @link
|
209 | }
|
210 |
|
211 | type Profile @entity(embedded: true) {
|
212 | name: String! @column
|
213 | age: Int! @column
|
214 | }
|
215 | ```
|
216 |
|
217 | The output will be:
|
218 |
|
219 | ```typescript
|
220 | import { ObjectID } from 'mongodb';
|
221 |
|
222 | export interface UserDbObject {
|
223 | _id: ObjectID;
|
224 | username: string;
|
225 | email: string;
|
226 | profile: ProfileDbObject;
|
227 | friends: ObjectID[];
|
228 | }
|
229 |
|
230 | export interface ProfileDbObject {
|
231 | name: string;
|
232 | age: string;
|
233 | }
|
234 | ```
|
235 |
|
236 | ## Generator Config
|
237 |
|
238 | This generator supports custom config and output behavior. Use to following flags/environment variables to modify your output as you wish:
|
239 |
|
240 | ### `schemaNamespace` (or `CODEGEN_SCHEMA_NAMESPACE`, default value: `null`)
|
241 |
|
242 | This will cause the codegen to wrap the generated schema typings with a TypeScript namespace.
|
243 |
|
244 | Use this feature if you need to run the codegen on multiple schemas, but getting a unified types (read more [here](https://www.typescriptlang.org/docs/handbook/declaration-merging.html))
|