Refactor database setup and add user context to GQL requests (#17)

* Add context to each request

* Implement get user db query

* Move constants to a separate file

* Refactor database init method
This commit is contained in:
neerajvijay1997
2024-02-01 11:37:57 +05:30
committed by Ashwin Phatak
parent 4d00cfb8f0
commit e4c099f8c3
7 changed files with 63 additions and 28 deletions
+3
View File
@@ -2,3 +2,6 @@
host = "127.0.0.1" host = "127.0.0.1"
port = 8000 port = 8000
gqlPath = "/graphql" gqlPath = "/graphql"
[database]
dbPath = "db/snowball"
@@ -4,6 +4,11 @@ export interface ServerConfig {
gqlPath?: string; gqlPath?: string;
} }
export interface DatabaseConfig {
dbPath: string;
}
export interface Config { export interface Config {
server: ServerConfig; server: ServerConfig;
database: DatabaseConfig;
} }
+4
View File
@@ -0,0 +1,4 @@
export const DEFAULT_GQL_PATH = '/graphql';
// Note: temporary hardcoded user, later to be derived from auth token
export const USER_ID = 1;
+25 -13
View File
@@ -2,23 +2,35 @@ import { DataSource } from 'typeorm';
import path from 'path'; import path from 'path';
import debug from 'debug'; import debug from 'debug';
const log = debug('snowball:server'); import { User } from './entity/User';
import { DatabaseConfig } from './config';
export const initializeDatabase = async ( const log = debug('snowball:database');
database: string = 'db/snowball'
): Promise<void> => { export class Database {
try { private dataSource: DataSource;
const AppDataSource = new DataSource({
constructor ({ dbPath }: DatabaseConfig) {
this.dataSource = new DataSource({
type: 'better-sqlite3', type: 'better-sqlite3',
database, database: dbPath,
entities: [path.join(__dirname, '/entity/*')], entities: [path.join(__dirname, '/entity/*')],
synchronize: true, synchronize: true,
logging: false logging: false
}); });
await AppDataSource.initialize();
log('database initialized');
} catch (error) {
log(error);
} }
};
async init () : Promise<void> {
await this.dataSource.initialize();
log('database initialized');
}
async getUser (userId: number) : Promise<User | null> {
const userRepository = this.dataSource.getRepository(User);
const user = await userRepository.findOneBy({
id: userId
});
return user;
}
}
+9 -5
View File
@@ -3,22 +3,26 @@ import debug from 'debug';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { initializeDatabase } from './database'; import { Database } from './database';
import { createAndStartServer } from './server'; import { createAndStartServer } from './server';
import { createResolvers } from './resolvers'; import { createResolvers } from './resolvers';
import { getConfig } from './utils'; import { getConfig } from './utils';
import { Config } from './type'; import { Config } from './config';
const log = debug('snowball:server'); const log = debug('snowball:server');
const configFilePath = 'environments/local.toml'; const configFilePath = 'environments/local.toml';
export const main = async (): Promise<void> => { export const main = async (): Promise<void> => {
// TODO: get config path using cli // TODO: get config path using cli
const { server } = await getConfig<Config>(configFilePath); const { server, database } = await getConfig<Config>(configFilePath);
const db = new Database(database);
await db.init();
await initializeDatabase();
const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString(); const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString();
await createAndStartServer(typeDefs, createResolvers, server); const resolvers = await createResolvers(db);
await createAndStartServer(typeDefs, resolvers, server);
}; };
main() main()
+10 -6
View File
@@ -1,12 +1,16 @@
const user = { import { Database } from './database';
id: 2
};
export const createResolvers = async (): Promise<any> => { export const createResolvers = async (db: Database): Promise<any> => {
return { return {
Query: { Query: {
// TODO: fetch user data from db user: (
user: () => user _: any,
__: any,
// TODO: add custom type for context
context: any
) => {
return db.getUser(context.userId);
}
} }
}; };
}; };
+7 -4
View File
@@ -10,12 +10,11 @@ import {
import { TypeSource } from '@graphql-tools/utils'; import { TypeSource } from '@graphql-tools/utils';
import { makeExecutableSchema } from '@graphql-tools/schema'; import { makeExecutableSchema } from '@graphql-tools/schema';
import { ServerConfig } from './type'; import { ServerConfig } from './config';
import { DEFAULT_GQL_PATH, USER_ID } from './constants';
const log = debug('snowball:server'); const log = debug('snowball:server');
const DEFAULT_GQL_PATH = '/graphql';
export const createAndStartServer = async ( export const createAndStartServer = async (
typeDefs: TypeSource, typeDefs: TypeSource,
resolvers: any, resolvers: any,
@@ -31,12 +30,16 @@ export const createAndStartServer = async (
// Create the schema // Create the schema
const schema = makeExecutableSchema({ const schema = makeExecutableSchema({
typeDefs, typeDefs,
resolvers: await resolvers() resolvers
}); });
const server = new ApolloServer({ const server = new ApolloServer({
schema, schema,
csrfPrevention: true, csrfPrevention: true,
context: () => {
// TODO: Use userId derived from auth token
return { userId: USER_ID };
},
plugins: [ plugins: [
// Proper shutdown for the HTTP server // Proper shutdown for the HTTP server
ApolloServerPluginDrainHttpServer({ httpServer }), ApolloServerPluginDrainHttpServer({ httpServer }),