├── .env.example ├── .gitignore ├── images ├── part1.jpg └── part2.jpg ├── nodemon.json ├── .xatarc ├── tsconfig.json ├── package.json ├── ReadMe.md └── src ├── xata.ts └── index.ts /.env.example: -------------------------------------------------------------------------------- 1 | XATA_API_KEY= 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | build 4 | .env -------------------------------------------------------------------------------- /images/part1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamesqquick/nodejs-api-with-rapidapi-typescript-xata/HEAD/images/part1.jpg -------------------------------------------------------------------------------- /images/part2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamesqquick/nodejs-api-with-rapidapi-typescript-xata/HEAD/images/part2.jpg -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": ".ts,.js", 4 | "ignore": [], 5 | "exec": "ts-node ./src/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /.xatarc: -------------------------------------------------------------------------------- 1 | { 2 | "databaseURL": "https://James-Q-Quick-s-workspace-l5kbra.us-east-1.xata.sh/db/jqq-job-board", 3 | "codegen": { 4 | "output": "src/xata.ts" 5 | } 6 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "outDir": "build", 5 | "rootDir": "src/", 6 | "allowSyntheticDefaultImports": true, 7 | "strict": true, 8 | "target": "es6", 9 | "lib": [ 10 | "esnext" 11 | ], 12 | "moduleResolution": "Node", 13 | "esModuleInterop": true, 14 | "module": "CommonJS" 15 | } 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-api-from-scratch-with-rapidapi-typescript-xata", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "engines": { 7 | "node": ">=18" 8 | }, 9 | "scripts": { 10 | "start:dev": "nodemon", 11 | "build": "npm install && rimraf ./build && tsc", 12 | "start": "node build/index.js " 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "devDependencies": { 18 | "@types/express": "^4.17.14", 19 | "@types/node": "^18.11.9", 20 | "nodemon": "^2.0.20", 21 | "rimraf": "^3.0.2", 22 | "ts-node": "^10.9.1", 23 | "typescript": "^4.8.4" 24 | }, 25 | "dependencies": { 26 | "@xata.io/client": "^0.21.3", 27 | "dotenv": "^16.0.3", 28 | "express": "^4.18.2", 29 | "node-fetch": "^3.2.10" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # Build a Node.js API with RapidAPI, TypeScript, and Xata.io 2 | 3 | This is the source code for a [multipart YouTube series](https://youtube.com/playlist?list=PLDlWc9AfQBfaaubcN_YPuq0NlERt1rHAM) where you will learn to build a RESTful TypeScript API for a job board using Express.js, RapidAPI, and Xata. 4 | 5 | ## Part 1 - Setup, Config, and CRUD 6 | 7 | [![Part 1 Cover](./images/part1.jpg)](https://youtu.be/8MjjmCQIdiY) 8 | 9 | ## [Part 2 - Type Safety and Error Handling](https://youtu.be/pNK09uh_tHs) 10 | 11 | [![Part 2 Cover](./images/part2.jpg)](https://youtu.be/pNK09uh_tHs) 12 | 13 | ## [Part 3 - Deploying to Render]() 14 | 15 | ## How To Use 16 | 17 | There is a branch for each corresponding YouTube video. The finished code for Part 1 is in the `part1...` branch, Part 2 is in the `part2...` branch, etc. 18 | 19 | ## Questions? 20 | 21 | If you have additional questions or feature requests, I recommend joining the [Learn Build Teach Discord server](https://learnbuildteach.com/) and asking there. 22 | -------------------------------------------------------------------------------- /src/xata.ts: -------------------------------------------------------------------------------- 1 | // Generated by Xata Codegen 0.18.0. Please do not edit. 2 | import { 3 | BaseClientOptions, 4 | buildClient, 5 | SchemaInference, 6 | XataRecord, 7 | } from '@xata.io/client'; 8 | 9 | const tables = [ 10 | { 11 | name: 'job', 12 | columns: [ 13 | { name: 'jobLink', type: 'string' }, 14 | { name: 'companyName', type: 'string' }, 15 | { name: 'jobTitle', type: 'string' }, 16 | { name: 'geography', type: 'string' }, 17 | ], 18 | }, 19 | ] as const; 20 | 21 | export type SchemaTables = typeof tables; 22 | export type InferredTypes = SchemaInference; 23 | 24 | export type Job = InferredTypes['job']; 25 | export type JobRecord = Job & XataRecord; 26 | 27 | export type DatabaseSchema = { 28 | job: JobRecord; 29 | }; 30 | 31 | const DatabaseClient = buildClient(); 32 | 33 | const defaultOptions = { 34 | databaseURL: 35 | 'https://James-Q-Quick-s-workspace-l5kbra.us-east-1.xata.sh/db/jqq-job-board', 36 | }; 37 | 38 | export class XataClient extends DatabaseClient { 39 | constructor(options?: BaseClientOptions) { 40 | super({ ...defaultOptions, ...options }, tables); 41 | } 42 | } 43 | 44 | let instance: XataClient | undefined = undefined; 45 | 46 | export const getXataClient = () => { 47 | if (instance) return instance; 48 | 49 | instance = new XataClient(); 50 | return instance; 51 | }; 52 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import express, { Express, Request, Response, urlencoded } from 'express'; 2 | import dotenv from 'dotenv'; 3 | import { getXataClient, Job } from './xata'; 4 | 5 | dotenv.config(); 6 | 7 | const app: Express = express(); 8 | app.use(express.json()); 9 | const port = process.env.PORT || 3000; 10 | const xata = getXataClient(); 11 | 12 | type MyResponse = { err: string } | { data: T }; 13 | 14 | app.get('/api/jobs', async (_: Request, res: Response>) => { 15 | try { 16 | const jobs = await xata.db.job.getAll(); 17 | return res.status(200).json({ data: jobs }); 18 | } catch (err) { 19 | console.error(err); 20 | return res.status(500).json({ err: 'Something went wrong' }); 21 | } 22 | }); 23 | 24 | app.post( 25 | '/api/jobs', 26 | async (req: Request<{}, {}, Job>, res: Response>) => { 27 | const job = req.body; 28 | try { 29 | const createdJob = await xata.db.job.create(job); 30 | return res.status(201).json({ data: createdJob }); 31 | } catch (err) { 32 | console.error(err); 33 | return res.status(500).json({ err: 'Something bad happened' }); 34 | } 35 | } 36 | ); 37 | 38 | app.put( 39 | '/api/jobs/:id', 40 | async ( 41 | req: Request<{ id: string }, {}, Job>, 42 | res: Response> 43 | ) => { 44 | const job = req.body; 45 | const id = req.params.id; 46 | try { 47 | const updatedJob = (await xata.db.job.update(id, job)) as Job; 48 | 49 | if (!updatedJob) { 50 | return res.status(404).json({ err: 'Job not found' }); 51 | } 52 | 53 | return res.status(200).json({ data: updatedJob }); 54 | } catch (err) { 55 | console.error(err); 56 | return res.status(500).json({ err: 'Something bad happened' }); 57 | } 58 | } 59 | ); 60 | 61 | app.delete( 62 | '/api/jobs/:id', 63 | async ( 64 | req: Request<{ id: string }, {}, {}>, 65 | res: Response> 66 | ) => { 67 | const id = req.params.id; 68 | try { 69 | const deletedRecord = (await xata.db.job.delete(id)) as Job; 70 | 71 | if (!deletedRecord) { 72 | return res.status(404).json({ err: 'Job not found' }); 73 | } 74 | 75 | return res.status(200).json({ data: deletedRecord }); 76 | } catch (err) { 77 | console.error(err); 78 | return res.status(500).json({ err: 'Something bad happened' }); 79 | } 80 | } 81 | ); 82 | 83 | app.listen(port, () => { 84 | console.log(`⚡️[server]: Port: ${port}`); 85 | }); 86 | --------------------------------------------------------------------------------