Skip to content

Instantly share code, notes, and snippets.

@lxchurbakov
Last active February 6, 2023 18:29
Show Gist options
  • Save lxchurbakov/4bc8977f16ced736c24618372709016d to your computer and use it in GitHub Desktop.
Save lxchurbakov/4bc8977f16ced736c24618372709016d to your computer and use it in GitHub Desktop.
starters
// tsconfig
{
"compilerOptions": {
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"baseUrl": "./src",
"outDir": "./dist",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es5",
"typeRoots": [
"node_modules/@types",
"src/types"
],
"lib": [
"es2018"
],
"paths": {
"/src/*": [
"./*"
],
}
},
"include": [
"./**/*.ts",
"./**/*.tsx",
]
}
// package.json
{
"scripts": {
"dev": "npx ts-node src/index.ts"
},
"devDependencies": {
"ts-node": "^10.9.1",
"tslib": "^2.4.1"
},
"dependencies": {
"express": "^4.18.2"
}
}
// index.ts
import express from 'express';
const app = express();
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept, authorization');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, OPTIONS, DELETE');
res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
app.use(express.json());
// Routes go here
app.get('/', (req, res) => {
res.send('Hello World!');
});
// Error handling
app.use((err, req, res, next) => {
if (!!err.statusCode) {
res.status(err.statusCode).json(err.body || null);
} else {
console.error(err.toString());
res.status(500).json('server_error');
}
});
// Start server
app.listen(3000, () => {
console.log('Server started on port 3000');
});
// utils.ts
export const route = (f) => (req, res, next) => {
try {
Promise.resolve(f(req, res)).then((d) => res.json(d)).catch((err) => next(err));
} catch (e) {
next(e);
}
};
export class HttpError extends Error {
constructor(public statusCode: number, public body: string, ...args: any[]) {
super(...args);
}
};
// tsconfig.json
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"baseUrl": "./src",
"outDir": "./dist",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types",
"src/types"
],
"lib": [
"es2018",
"dom"
],
"jsx": "react",
"paths": {
"/src/*": [
"./*"
],
}
},
"include": [
"./**/*.ts",
"./**/*.tsx",
]
}
// package.json
"scripts": {
"build": "npx parcel build --public-url ./ src/index.html",
"dev": "npx parcel serve src/index.html"
},
"dependencies": {
"parcel": "^2.8.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.4.3"
},
"devDependencies": {
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.9"
}
// index.ts
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import SomePage from './page';
const app = document.getElementById('app');
ReactDOM.render((
<BrowserRouter>
<Routes>
<Route path="/" element={<SomePage />} />
</Routes>
</BrowserRouter>
), app);
// index.html
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;700;900&display=swap" rel="stylesheet">
<style>body { margin: 0; padding: 0 } </style>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div id="app"></div>
<script type="module" src="index.tsx"></script>
</body>
</html>
// mongo
const { MongoClient, ObjectId } = require('mongodb');
const URI = 'mongodb://root:example@localhost:27017/?maxPoolSize=20&w=majority';
const client = new MongoClient(URI);
const database = client.db('learning');
export { ObjectId };
export const Knowledge = database.collection('knowledge');
client.connect().catch((error) => {
console.error(error);
process.exit(1);
});
// router
const _ = require('lodash');
const express = require('express');
const { route, HttpError } = require('../utils');
const { Knowledge, ObjectId } = require('../mongo');
const router = express.Router({ mergeParams: true });
router.post('/', route(async (req) => {
return Knowledge.insertOne(_.pick(req.body, [
'title', 'description', 'content'
])).then(({ insertedId }) => {
return insertedId;
});
}));
router.get('/', route(async (req) => {
const skip = Number(req.query.skip);
const limit = Number(req.query.limit);
if (isNaN(skip) || isNaN(limit) || skip < 0 || limit < 1) {
throw new HttpError(400, 'invalid_params');
}
return Knowledge.find({}).skip(skip).limit(limit).toArray();
}));
router.route('/:id')
.all((req, res, next) => {
const id = req.params.id;
if (!id) {
throw new HttpError(404, 'not_found');
}
next();
})
.get(route(async (req) => {
const knowledge = await Knowledge.findOne({ _id: ObjectId(req.params.id) });
if (!knowledge) {
throw new HttpError(404, 'not_found');
}
return knowledge;
}))
.put(route(async (req) => {
const { value } = await Knowledge.findOneAndUpdate({
_id: ObjectId(req.params.id)
}, {
$set: _.pick(req.body, ['title', 'description', 'content'])
});
if (!value) {
throw new HttpError(404, 'not_found');
}
}))
.delete(route(async (req) => {
const { deletedCount } = await Knowledge.deleteOne({ _id: ObjectId(req.params.id) })
if (deletedCount === 0) {
throw new HttpError(404, 'not_found');
}
}));
export { router };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment