Build a SQLite CRUD API with Deno: A Step-by-Step Guide
Creating a CRUD (Create, Read, Update, Delete) application using SQLite in a Deno environment is a great way to understand both SQLite as a database and Deno as a runtime environment. Here's a step-by-step guide to help you build this application.
Prerequisites:
- You should have Deno installed on your machine.
- Basic understanding of JavaScript/TypeScript.
Step 1: Setup the Project
-
Initialize Deno Project: Create a directory for your project.
mkdir deno-sqlite-crud cd deno-sqlite-crud
-
Create Files: Create the main file
app.ts
, and other necessary files such asdb.ts
for handling database connections and queries.
Step 2: Setup Database
-
Install SQLite Driver: Deno requires third-party permissions for file access and network access, for security purposes. While there is no need to 'install' modules like Node.js, you import them directly. For SQLite, we'll use a third-party module.
Create
import_map.json
to manage dependencies cleanly:{ "imports": { "sqlite": "https://deno.land/x/sqlite/mod.ts" } }
Then run:
deno run --unstable --import-map=import_map.json --allow-read --allow-write app.ts
-
Setup Database Connection in
db.ts
:import { DB } from "sqlite"; const db = new DB("tasks.db"); db.query(` CREATE TABLE IF NOT EXISTS tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, completed BOOLEAN ) `); export default db;
Step 3: CRUD Operations
-
Create CRUD operations in
db.ts
:// Create export const createTask = (title: string) => { db.query("INSERT INTO tasks (title, completed) VALUES (?, ?)", [title, false]); }; // Read export const getTasks = () => { return [...db.query("SELECT id, title, completed FROM tasks")].map(([id, title, completed]) => ({ id, title, completed, })); }; // Update export const updateTask = (id: number, completed: boolean) => { db.query("UPDATE tasks SET completed = ? WHERE id = ?", [completed, id]); }; // Delete export const deleteTask = (id: number) => { db.query("DELETE FROM tasks WHERE id = ?", [id]); };
Step 4: Setup a Basic API
-
Create a basic server in
app.ts
:import { serve } from "https://deno.land/std@0.106.0/http/server.ts"; import { createTask, getTasks, updateTask, deleteTask } from "./db.ts"; const server = serve({ port: 8000 }); console.log("Server running on http://localhost:8000/"); for await (const req of server) { const url = new URL(req.url, `http://localhost`); let bodyContent = ""; if (req.method === "GET" && url.pathname === "/tasks") { const tasks = getTasks(); bodyContent = JSON.stringify(tasks); } else if (req.method === "POST" && url.pathname === "/tasks") { const decoder = new TextDecoder(); const body = decoder.decode(await Deno.readAll(req.body)); const { title } = JSON.parse(body); createTask(title); req.respond({ status: 201, body: JSON.stringify({ message: "Task created" }) }); continue; } else if (req.method === "PUT" && url.pathname.startsWith("/tasks/")) { const id = parseInt(url.pathname.split("/")[2]); const decoder = new TextDecoder(); const body = decoder.decode(await Deno.readAll(req.body)); const { completed } = JSON.parse(body); updateTask(id, completed); req.respond({ status: 200, body: JSON.stringify({ message: "Task updated" }) }); continue; } else if (req.method === "DELETE" && url.pathname.startsWith("/tasks/")) { const id = parseInt(url.pathname.split("/")[2]); deleteTask(id); req.respond({ status: 200, body: JSON.stringify({ message: "Task deleted" }) }); continue; } else { req.respond({ status: 404, body: "Not Found" }); continue; } req.respond({ status: 200, body: bodyContent }); }
Step 5: Running Your Application
Run the application by enabling read and write access for the database file.
deno run --allow-net --allow-read --allow-write --import-map=import_map.json app.ts
Notes:
- Security: The above setup is suitable for local development. For production, tightening security permissions and handling CORS properly is crucial.
- Robustness: Add error handling and more sophisticated logic to parse inputs correctly, handle edge cases, and avoid SQL injections.
- Cleaning Up: Ensure to properly close the database connection if needed when shutting down the server.
This simple CRUD application using Deno and SQLite should give you a foundational understanding of connecting these technologies and building a simple API. You can further expand it with more features, authentication, or even integrate with a front-end framework.