Created
July 15, 2021 18:53
-
-
Save marcj/4ea2a6f45888b637a6ad72cc8ab41d84 to your computer and use it in GitHub Desktop.
Deepkit REST crud automatic controller
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ts-node-script | |
import 'reflect-metadata'; | |
import { Application } from '@deepkit/framework'; | |
import { http } from '@deepkit/http'; | |
import { ClassSchema, entity, getClassSchema, t } from '@deepkit/type'; | |
import { Database } from '@deepkit/orm'; | |
import { MongoDatabaseAdapter } from '@deepkit/mongo'; | |
import { ClassType, getObjectKeysSize } from '@deepkit/core'; | |
import { AppModule } from '@deepkit/app'; | |
export const emailValidation = /^\S+@\S+$/; | |
@entity.name('group') | |
class Group { | |
@t.autoIncrement.primary public id: number = 0; | |
constructor( | |
@t public name: string, | |
) { | |
} | |
} | |
@entity.name('user') | |
class User { | |
@t.autoIncrement.primary public id: number = 0; | |
@t.pattern(emailValidation) email?: string; | |
@t.reference() group?: Group; | |
constructor( | |
@t public username: string, | |
) { | |
} | |
} | |
class MainDatabase extends Database { | |
constructor() { | |
super(new MongoDatabaseAdapter('mongodb://localhost/dynamic'), [User, Group]); | |
} | |
} | |
function createController(schema: ClassSchema): ClassType { | |
if (!schema.name) throw new Error(`Class ${schema.getClassName()} needs an entity name via @entity.name()`); | |
const primaryKey = schema.getPrimaryField(); | |
class ListQuery { | |
@t.partial(schema) filter?: Partial<any>; | |
@t select?: string; | |
@t.map(t.union('asc', 'desc')) orderBy: { [name: string]: 'asc' | 'desc' } = {}; | |
@t.map(t.string) joins: { [name: string]: string } = {}; | |
@t offset: number = 0; | |
@t limit: number = 0; | |
} | |
@http.controller('/entity/' + schema.name) | |
class RestController { | |
constructor(protected database: MainDatabase) { | |
} | |
@http.GET('') | |
async list(@http.queries() options: ListQuery) { | |
options.limit = Math.min(100, options.limit); //max 100 | |
let query = this.database.query(schema); | |
for (const field of Object.keys(query.orderBy)) { | |
if (!schema.hasProperty(field)) throw new Error(`Field ${field} does not exist`); | |
} | |
for (const [field, projection] of Object.entries(options.joins)) { | |
if (!schema.hasProperty(field)) throw new Error(`Join ${field} does not exist`); | |
let join = query.useJoinWith(field); | |
if (projection.length) { | |
join = join.select(...projection.split(',')); | |
} | |
query = join.end(); | |
} | |
if (options.select) query = query.select(...options.select.split(',')); | |
if (getObjectKeysSize(query.orderBy) > 0) query.model.sort = query.orderBy; | |
return await query | |
.filter(options.filter) | |
.limit(options.limit) | |
.skip(options.offset) | |
.find(); | |
} | |
@http.POST('').description('Adds a new ' + schema.name) | |
async post(@t.type(schema) @http.body() body: any) { | |
//body is automatically validated | |
await this.database.persist(body); | |
return { [primaryKey.name]: body[primaryKey.name] }; | |
} | |
@http.DELETE(':id').description('Delete a ' + schema.name) | |
async remove(id: number) { | |
const result = await this.database.query(schema).filter({ [primaryKey.name]: id }).deleteOne(); | |
return {deleted: result.modified}; | |
} | |
@http.PUT(':id').description('Updates ' + schema.name) | |
async put(id: number, @t.type(schema) @http.body() body: any) { | |
const item = await this.database.query(schema).filter({ [primaryKey.name]: id }).findOne(); | |
delete body[primaryKey.name]; //we dont allow to change primary | |
Object.assign(item, body); | |
await this.database.persist(item); | |
return true; | |
} | |
} | |
Object.defineProperty(RestController, 'name', { value: 'RestController' + schema.getClassName() }); | |
return RestController; | |
} | |
function createDynamicRoutes(schemas: ClassType[]) { | |
const controllers = schemas.map(getClassSchema).map(createController); | |
return new AppModule({ | |
controllers: controllers | |
} | |
); | |
} | |
Application.create({ | |
providers: [MainDatabase], | |
imports: [createDynamicRoutes([User, Group])] | |
}).run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment