Skip to content

Commit

Permalink
Merge pull request #5 from Selleo/api-basics
Browse files Browse the repository at this point in the history
Api basics
  • Loading branch information
k1eu authored Jul 8, 2024
2 parents 2a56710 + b9ad2fe commit 21b29ee
Show file tree
Hide file tree
Showing 33 changed files with 1,532 additions and 79 deletions.
1 change: 1 addition & 0 deletions examples/common_nestjs_remix/apps/api/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DATABASE_URL="postgres://postgres:guidebook@localhost:5432/guidebook"
4 changes: 2 additions & 2 deletions examples/common_nestjs_remix/apps/api/.prettierrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"singleQuote": true,
"singleQuote": false,
"trailingComma": "all"
}
}
12 changes: 12 additions & 0 deletions examples/common_nestjs_remix/apps/api/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineConfig } from "drizzle-kit";

export default defineConfig({
schema: "./src/storage/schema",
out: "./src/storage/migrations",
dialect: "postgresql",
dbCredentials: {
url: "postgres://postgres:guidebook@localhost:5432/guidebook",
},
verbose: true,
strict: true,
});
14 changes: 13 additions & 1 deletion examples/common_nestjs_remix/apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,24 @@
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
"test:e2e": "jest --config ./test/jest-e2e.json",
"db:migrate": "drizzle-kit migrate",
"db:generate": "drizzle-kit generate"
},
"dependencies": {
"@knaadh/nestjs-drizzle-postgres": "^1.0.0",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.0.0",
"@nestjs/cqrs": "^10.2.7",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.4.0",
"@sinclair/typebox": "^0.32.34",
"drizzle-kit": "^0.22.8",
"drizzle-orm": "^0.31.2",
"drizzle-typebox": "^0.1.1",
"nestjs-typebox": "3.0.0-next.8",
"postgres": "^3.4.4",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
Expand Down
22 changes: 0 additions & 22 deletions examples/common_nestjs_remix/apps/api/src/app.controller.spec.ts

This file was deleted.

12 changes: 0 additions & 12 deletions examples/common_nestjs_remix/apps/api/src/app.controller.ts

This file was deleted.

35 changes: 29 additions & 6 deletions examples/common_nestjs_remix/apps/api/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Module } from "@nestjs/common";
import { DrizzlePostgresModule } from "@knaadh/nestjs-drizzle-postgres";
import database from "./common/configuration/database";
import { ConfigModule, ConfigService } from "@nestjs/config";
import * as schema from "./storage/schema";
import { ManagementModule } from "./management/management.module";

@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
imports: [
ConfigModule.forRoot({
load: [database],
isGlobal: true,
}),
DrizzlePostgresModule.registerAsync({
tag: "DB",
useFactory(configService: ConfigService) {
return {
postgres: {
url: configService.get<string>("database.url")!,
},
config: {
schema: { ...schema },
},
};
},
inject: [ConfigService],
}),
ManagementModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
8 changes: 0 additions & 8 deletions examples/common_nestjs_remix/apps/api/src/app.service.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { registerAs } from "@nestjs/config";
import { Static, Type } from "@sinclair/typebox";
import { Value } from "@sinclair/typebox/value";

const schema = Type.Object({
url: Type.String(),
});

type DatabaseConfig = Static<typeof schema>;

export default registerAs("database", (): DatabaseConfig => {
const values = {
url: process.env.DATABASE_URL,
};

return Value.Decode(schema, values);
});
25 changes: 25 additions & 0 deletions examples/common_nestjs_remix/apps/api/src/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { TObject, Type } from "@sinclair/typebox";
import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
import * as schema from "src/storage/schema";

export type DatabasePg = PostgresJsDatabase<typeof schema>;

export class BaseResponse<T> {
data: T;

constructor(data: T) {
this.data = data;
}
}

export const UUIDSchema = Type.String({ format: "uuid" });

export function baseResponse(data: TObject) {
return Type.Object({
data,
});
}

export function nullResponse() {
return Type.Null();
}
20 changes: 18 additions & 2 deletions examples/common_nestjs_remix/apps/api/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { patchNestJsSwagger, applyFormats } from "nestjs-typebox";
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
import { exportSchemaToFile } from "./utils/save-swagger-to-file";

patchNestJsSwagger();
applyFormats();

async function bootstrap() {
const app = await NestFactory.create(AppModule);

const config = new DocumentBuilder()
.setTitle("Guidebook API")
.setDescription("Example usage of Swagger with Typebox")
.setVersion("1.0")
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup("api", app, document);
exportSchemaToFile(document);

await app.listen(3000);
}
bootstrap();
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Controller, Delete, Post } from "@nestjs/common";
import { Validate } from "nestjs-typebox";
import { propertySchema } from "../schemas/property";
import { createPropertySchema } from "../schemas/create-property";
import { Static } from "@sinclair/typebox";
import { PropertiesService } from "../properties.service";
import {
BaseResponse,
UUIDSchema,
baseResponse,
nullResponse,
} from "src/common";

@Controller("properties")
export class PropertiesController {
constructor(private readonly propertiesService: PropertiesService) {}

@Post()
@Validate({
response: baseResponse(propertySchema),
request: [{ type: "body", schema: createPropertySchema }],
})
async createProperty(
data: Static<typeof createPropertySchema>,
): Promise<BaseResponse<Static<typeof propertySchema>>> {
const property = await this.propertiesService.createProperty(data);

return new BaseResponse(property);
}

@Delete(":id")
@Validate({
response: nullResponse(),
request: [{ name: "id", type: "param", schema: UUIDSchema }],
})
async deleteProperty(id: string): Promise<null> {
await this.propertiesService.deleteProperty(id);
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from "@nestjs/common";
import { PropertiesController } from "./api/properties.controller";
import { PropertiesService } from "./properties.service";

@Module({
imports: [],
controllers: [PropertiesController],
providers: [PropertiesService],
exports: [],
})
export class ManagementModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Inject, Injectable, NotFoundException } from "@nestjs/common";
import { eq } from "drizzle-orm";
import { DatabasePg } from "src/common";
import { properties } from "src/storage/schema";

@Injectable()
export class PropertiesService {
constructor(@Inject("DB") private readonly db: DatabasePg) {}

async createProperty(data: { name: string; description?: string }) {
const [property] = await this.db
.insert(properties)
.values({
name: data.name,
description: data.description,
})
.returning();

return property;
}

async deleteProperty(id: string): Promise<void> {
const [deleted] = await this.db
.delete(properties)
.where(eq(properties.id, id));

if (!deleted) {
throw new NotFoundException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Type } from "@sinclair/typebox";

export const createPropertySchema = Type.Object({
name: Type.String(),
description: Type.Optional(Type.String()),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createSelectSchema } from "drizzle-typebox";
import { properties } from "src/storage/schema";

export const propertySchema = createSelectSchema(properties);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"id": "e69dc2c4-c21a-4d2a-bceb-820005722d28",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"created_at": {
"name": "created_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_TIMESTAMP"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_TIMESTAMP"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1720182483074,
"tag": "0000_create_users",
"breakpoints": true
}
]
}
15 changes: 15 additions & 0 deletions examples/common_nestjs_remix/apps/api/src/storage/schema/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { pgTable, text } from "drizzle-orm/pg-core";
import { id, timestamps } from "./utils";

export const users = pgTable("users", {
...id,
...timestamps,
});

export const properties = pgTable("properties", {
...id,
...timestamps,

name: text("name").notNull(),
description: text("description"),
});
Loading

0 comments on commit 21b29ee

Please sign in to comment.