Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
dotnet new list
dotnet new webapi -n MyWebApi
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.Run();
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
// imports useful methods for setting up a controller
using Microsoft.AspNetCore.Mvc;
// sets up an api controller
namespace MyApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class HelloController : ControllerBase
{
// creates our get route
[HttpGet]
public string Get()
{
//decides what is returned by the route
return "Hello, World!";
}
}
}
dotnet rundotnet new gitignore Learn how to safely store passwords, and identify returning users with cookies and sessions
Secure will ensure the cookie is only set for encrypted (https) connections. You shouldn't use this in development (since localhost doesn't use https) but it's a good idea in production.const crypto = require("node:crypto");
const password = "hunter2";
// Hash string with the SHA256 algorithm and output in hexadecimal format
const hashed = crypto.createHash("sha256").update(password).digest("hex");
// "f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7"const bcrypt = require("bcryptjs");
const password = "hunter2";
bcrypt.hash(password, 12).then((hash) => console.log(hash));
// $2a$10$n1etzOWCrAtJGQIDoaw0mun1ojnIjA2UaiJ8DkL76ljhGa/cZCQtq//...
bcrypt.hash(password, cost).then((hash) => {
bcrypt.compare("hunter2", hash).then((result) => console.log(result));
// true
bcrypt.compare("incorrect", hash).then((result) => console.log(result));
// false
});HTTP/1.1 200 Ok
content-type: text/html
set-cookie: userid=1234
<h1>Hello</h1>GET /about HTTP/1.1
accept: text/html
cookie: userid=1234set-cookie: userid=1234; Max-Age=60; HttpOnly; SameSite=Lax// responding to one request...
response.set("set-cookie", "hello=world; HttpOnly; Max-Age=60; SameSite=Lax");
// reading a later request...
console.log(request.get("cookie"));
// "hello=world; HttpOnly; Max-Age=60; SameSite=Lax"response.cookie("hello", "world", {
httpOnly: true,
maxAge: 6000,
sameSite: "lax",
});
// This sets the same `set-cookie` header as beforeresponse.clearCookie("hello");
// This sets a header that deletes the previous cookie in the browserconst cookieParser = require("cookie-parser");
server.use(cookieParser());
// Reading a later request...
console.log(request.cookies);
// { hello: "world" }server.use(cookieParser("random-string-that-should-be-an-env-var"));
// ...
response.cookie("hello", "world", {
signed: true,
httpOnly: true,
maxAge: 6000,
sameSite: "lax",
});// Reading a later request...
console.log(request.signedCookies);
// { hello: "world" }-- schema.sql
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
-- plus other columns...
);
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
expires_at DATETIME NOT NULL
)// model.js
const crypto = require("node:crypto");
// Sets the expiry to current date + 7 days
const insert_session = db.prepare(`INSERT INTO sessions VALUES (
$id,
$user_id,
DATE('now', '+7 days')
)`);
function createSession(user_id) {
// quick way to generate a random string in Node
const id = crypto.randomBytes(18).toString("base64");
insert_session.run({ id, user_id });
// return the generated ID so we can store in a cookie
return id;
}// model.js
const select_session = db.prepare(`
SELECT id, user_id, expires_at
FROM sessions WHERE id = ?
`);
function getSession(sid) {
return select_session.get(sid);
}// routes/private.js
function get(req, res) {
const sid = req.signedCookies.sid;
const session = model.getSession(sid);
if (session && session.user_id) {
// we have a logged in user
res.send("<h1>Private stuff</h1>");
} else {
// request is not authenticated
res.status(401).send("<h1>Please log in to view this page</h1>");
}
}namespace MyWebApi.Models
{
public class Book
{
public Guid Id {set; get;}
public string? Name {set; get;}
}
}
dotnet add package Microsoft.EntityFrameworkCore
using Microsoft.EntityFrameworkCore;
using MyWebApi.Models;
namespace MyWebApi.Data
{
public class BookContext : DbContext
{
public BookContext(DbContextOptions options): base(options){}
public DbSet<Book> Books {set; get;}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Book>().HasData(
new Book {Id = Guid.NewGuid(), Name = "The Prospects"},
new Book {Id = Guid.NewGuid(), Name = "Nevada"}
);
base.OnModelCreating(modelBuilder);
}
}
}
dotnet add package Microsoft.EntityFrameworkCore.InMemory using Microsoft.EntityFrameworkCore;
using MyWebApi.Data;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<BookContext>(options => options.UseInMemoryDatabase("book db"));
builder.Services.AddControllers();
var app = builder.Build();
using(var scope = app.Services.CreateScope())
using(var db = scope.ServiceProvider.GetService<BookContext>()!)
{
db.Database.EnsureCreated();
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapControllerRoute("default", "api/[controller]/[action]");
app.UseHttpsRedirection();
app.Run();
using Microsoft.AspNetCore.Mvc;
using MyWebApi.Data;
using MyWebApi.Models;
namespace MyWebApi.Controllers
{
[ApiController]
[Route("api/[controller]/[action]")]
public class BookController : Controller
{
private readonly BookContext _context;
public BookController(BookContext context)
{
_context = context;
}
[HttpGet]
public List<Book> GetAllBooks()
{
var result = _context.Books.ToList();
return result;
}
[HttpGet]
public Book GetABook(string name)
{
var result = _context.Books.FirstOrDefault<Book>((book)=> book.Name== name);
if (result == null)
throw new Exception("book not found");
return result;
}
}
}
public class BookController : Controller
{
private readonly BookContext _context;
public BookController(BookContext context)
{
_context = context;
}using MyWebApi.Models;
using Microsoft.EntityFrameworkCore;
namespace MyWebApi.Data
{
public interface IBookContext
{
public DbSet<Book> Books {set; get;}
}
}using Microsoft.EntityFrameworkCore;
using MyWebApi.Models;
namespace MyWebApi.Data
{
public class BookContext : DbContext ,IBookContext
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<IBookContext,BookContext>(options => options.UseInMemoryDatabase("book db"));
builder.Services.AddControllers();
public class BookController : Controller
{
private readonly IBookContext _context;
public BookController(IBookContext context)
{
public class BookControllerTests
{
private readonly BookController _controller;
public BookControllerTests()
{
var fakeData = new List<Book>
{
new Book {Id = Guid.NewGuid(), Name = "Fake book1" },
new Book {Id = Guid.NewGuid(), Name = "Fake book2" }
}.AsQueryable();
var _context = new Mock<IBookContext>();
_context.Setup(db => db.Books).ReturnsDbSet(fakeData);
_controller = new BookController(_context.Object);
}
[Fact]
public void GetAllBooks_Returns_Entire_List()
{
var expected = new List<Book>
{
new Book {Id = Guid.NewGuid(), Name = "Fake book1" },
new Book {Id = Guid.NewGuid(), Name = "Fake book2" }
}.AsQueryable();
var result = _controller.GetAllBooks().ToList();
Assert.Equivalent(expected, result);
}
[Fact]
public void GetABook_Returns_A_Book()
{
var expected = new Book {Id = Guid.NewGuid(), Name = "Fake book1" };
var result = _controller.GetABook("Fake book1");
Assert.Equivalent(expected, result);
}
}
}var _context = new Mock<IBookContext>();
_context.Setup(db => db.Books).ReturnsDbSet(fakeData);
_controller = new BookController(_context.Object);Learn how to use the SQLite database to persist data for your Node apps
mkdir learn-database
cd learn-database
npm init -ynpm install better-sqlite3const Database = require("better-sqlite3");
const db = new Database();
console.log(db);node database/db.jsconst db = new Database("db.sqlite");const db = new Database(process.env.DB_FILE);DB_FILE=db.sqlite node database/db.jsconst select_date = db.prepare("SELECT DATE()");
console.log(select_date);const select_date = db.prepare("SELECT DATE()");
const result = select_date.get();
console.log(result);{ "DATE()": "2022-09-13" }BEGIN;
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
COMMIT;const { readFileSync } = require("node:fs");
const { join } = require("node:path");
const Database = require("better-sqlite3");
const db = new Database(process.env.DB_FILE);
const schemaPath = join("database", "schema.sql");
const schema = readFileSync(schemaPath, "utf-8");
db.exec(schema);const select_table = db.prepare("SELECT name FROM sqlite_schema");
const result = select_table.all();
console.log(result);[ { name: 'tasks' }, ... ]const { readFileSync } = require("node:fs");
const { join } = require("node:path");
const Database = require("better-sqlite3");
/**
* If we do not set DB_FILE env var creates an in-memory temp DB.
* Otherwise connect to the DB contained in the file we specified (if it exists).
* If it does not exist create a new DB file and connect to it.
*/
const db = new Database(process.env.DB_FILE);
/**
* Make sure DB has the right structure by running schema.sql
* This is responsible for creating the tables and columns we need
* It should be safe to run every time
*/
const schemaPath = join("database", "schema.sql");
const schema = readFileSync(schemaPath, "utf-8");
db.exec(schema);
/**
* Export the DB for use in other files
*/
module.exports = db;const db = require("../database/db.js");
const insert_task = db.prepare("INSERT INTO tasks (content) VALUES (?)");
function createTask(content) {
insert_task.run(content);
}
module.exports = { createTask };// ...
createTask("Eat a banana");
const tasks = db.prepare("SELECT * FROM tasks").all();
console.log(tasks);[{ "id": 1, "content": "Eat a banana", "created_at": "2022-09-14 08:40:41" }]const insert_task = db.prepare(`
INSERT INTO tasks (content)
VALUES (?)
RETURNING id, content, created_at
`);
function createTask(content) {
return insert_task.get(content);
}const result = createTask("Send mum flowers");
console.log(result);{ "id": 2, "content": "Send mum flowers", "created_at": "2022-09-14 08:52:30" }BEGIN;
INSERT INTO tasks VALUES
(1, 'Create my first todo', '2022-09-16 01:01:01'),
(2, 'Buy milk', '2022-09-16 11:10:07'),
(3, 'Become a 10x developer', '2022-09-16 23:59:59')
ON CONFLICT(id) DO NOTHING;
COMMIT;const { readFileSync } = require("node:fs");
const { join } = require("node:path");
const db = require("./db.js");
const seedPath = join("database", "seed.sql");
const seed = readFileSync(seedPath, "utf-8");
db.exec(seed);
console.log("DB seeded with example data");{
"scripts": {
"seed": "DB_FILE=db.sqlite node database/seed.js"
}
}CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
complete INTEGER DEFAULT 0 CHECK(complete IN (0, 1))
);INSERT INTO tasks VALUES
(1, 'Create my first todo', '2022-09-16 01:01:01', 1),
(2, 'Buy milk', '2022-09-16 11:10:07', 0),
(3, 'Become a 10x developer', '2022-09-16 23:59:59', 1)
ON CONFLICT(id) DO NOTHING;const insert_task = db.prepare(`
INSERT INTO tasks (content, complete)
VALUES (?, ?)
RETURNING id, content, created_at
`);
function createTask(content, complete) {
return insert_task.get(content, complete);
}const insert_task = db.prepare(`
INSERT INTO tasks (content, complete)
VALUES ($content, $complete)
RETURNING id, content, created_at
`);
function createTask(task) {
return insert_task.get(task);
}const select_tasks = db.prepare(/*sql*/ `
SELECT id, content, created_at, complete FROM tasks
`);
function listTasks() {
return select_tasks.all();
}const select_tasks = db.prepare(/*sql*/ `
SELECT
id,
content,
TIME(created_at),
complete
FROM tasks
`);[
{
"id": 2,
"content": "Send mum flowers",
"TIME(created_at)": "08:52:30",
"complete": 0
}
]const select_tasks = db.prepare(/*sql*/ `
SELECT
id,
content,
TIME(created_at) AS created_at,
complete
FROM tasks
`);[
{
"id": 2,
"content": "Send mum flowers",
"created_at": "08:52:30",
"complete": 0
}
]const delete_task = db.prepare(/*sql*/ `
DELETE FROM tasks WHERE id = ?
`);
function removeTask(id) {
delete_task.run(id);
}const test = require("node:test");
const assert = require("node:assert");
const model = require("../model/tasks.js");
const db = require("../database/db.js");
// Delete all tasks and reset ID counter
function reset() {
db.exec(/*sql*/ `
DELETE FROM tasks;
DELETE FROM sqlite_sequence WHERE name='tasks';
`);
}
test("can create, remove & list tasks", () => {
reset();
const task = model.createTask({ content: "test task", complete: 0 });
assert.equal(task.id, 1);
assert.equal(task.content, "test task");
model.removeTask(task.id);
const tasks = model.listTasks();
assert.equal(tasks.length, 0);
});DB_FILE=test.sqlite node test/tasks.test.jsDB_FILE=test.sqlite node -r ./database/seed.js test/tasks.test.jsconst update_content = db.prepare(/*sql*/ `
UPDATE tasks
SET content = $content
WHERE id = $id
RETURNING id, content, created_at, complete
`);
function editTask(task) {
return update_content.get(task);
}test("can update a task", () => {
reset();
const task = model.createTask({ content: "test task", complete: 0 });
const updated = model.editTask({ id: 1, content: "this is updated" });
assert.equal(updated.id, 1);
assert.equal(updated.content, "this is updated");
});const update_complete = db.prepare(/*sql*/ `
UPDATE tasks
SET complete = NOT complete
WHERE id = ?
RETURNING id, content, created_at, complete
`);
function toggleTask(id) {
return update_complete.get(id);
}test("can complete a task", () => {
reset();
const task = model.createTask({ content: "test task", complete: 0 });
const updated = model.toggleTask(1);
assert.equal(updated.complete, 1);
});npm install expressconst express = require("express");
const server = express();
server.get("/", (req, res) => {
res.send("hello world");
});
module.exports = server;const server = require("./server.js");
const PORT = process.env.PORT || 3333;
server.listen(PORT, () => console.log(`Listening on http://localhost:${PORT}`));server.get("/", (req, res) => {
const body = /*html*/ `
<!doctype html>
<form method="POST">
<input id="content" name="content" aria-label="New task" required>
<button>Add task +</button>
</form>
`;
res.send(body);
});const model = require("./model/tasks.js");
server.post("/", express.urlencoded({ extended: false }), (req, res) => {
const task = {
content: req.body.content,
complete: 0,
};
model.createTask(task);
res.redirect("/");
});DB_FILE=db.sqlite node index.js{
"scripts": {
"dev": "DB_FILE=db.sqlite node index.js"
}
}server.get("/", (req, res) => {
const tasks = model.listTasks();
const body = /*html*/ `
<!doctype html>
<form method="POST">
<input id="content" name="content" aria-label="New task" required>
<button>Add task +</button>
</form>
<ul>${tasks.map((t) => `<li>${t.content}</li>`).join("")}</ul>
`;
res.send(body);
});server.post("/update", express.urlencoded({ extended: false }), (req, res) => {
const { action, id } = req.body;
if (action === "remove") model.removeTask(id);
if (action === "toggle") model.toggleTask(id);
res.redirect("/");
});function Task(task) {
return /*html*/ `
<li>
<form method="POST" action="/update">
<input type="hidden" name="id" value="${task.id}">
<button name="action" value="toggle" aria-label="Toggle complete">
${task.complete ? "☑︎" : "☐"}
</button>
<span style="${task.complete ? "text-decoration: line-through" : ""}">
${task.content}
</span>
<button name="action" value="remove">×</button>
</form>
</li>
`;
}
server.get("/", (req, res) => {
const tasks = model.listTasks();
const list = tasks.map(Task);
const body = /*html*/ `
<!doctype html>
<form method="POST">
<input id="content" name="content" aria-label="New task" required>
<button>Add task +</button>
</form>
<ul>${list.join("")}</ul>
`;
res.send(body);
});id=1&action=toggleid=1&action=removeLearn how to validate user input in the browser and present error messages accessibly.
Learn how to create client-side apps using React and TypeScript
children prop<!-- `for` attribute associates label with input by ID -->
<label for="name">What is your name?</label>
<input id="name" />import React, { useState } from "react";
import ReactDOM from "react-dom/client";
function Counter() {
// Calling the `setCount` with a new value re-runs your component
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
// Any properties passed to the component are available on the `props` object
function Title(props: { id: string; children: React.ReactNode }) {
return <h1 id={props.id}>{props.children}</h1>;
}
function App() {
return (
<div>
<Title id="main-title">Hello world</Title>
<Counter />
</div>
);
}
// React handles all DOM element creation/updates—you just call `render` once
const root = ReactDOM.createRoot(document.querySelector("#root")!);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);<label for="name">
What is your name?
<span aria-hidden="true">*</span>
</label>
<input id="name" required /><label for="password">New password</label>
<p id="passwordHelp">Your password must be at least 10 characters long</p>
<input id="password" aria-describedby="passwordHelp" /><input required /><!-- checks the value is an email string -->
<input type="email" required />
<!-- checks the value is a URL string -->
<input type="url" required /><input type="text" pattern="\S" />const form = document.querySelector("form");
form.setAttribute("novalidate", "");form.addEventListener("submit", (event) => {
const allValid = form.checkValidity();
if (!allValid) {
event.preventDefault();
}
});const fields = form.querySelectorAll("input"); // you probably want to include <select>, <textarea> etc too
fields.forEach((field) => {
field.setAttribute("aria-invalid", "false");
});fields.forEach((field) => {
// ...
field.addEventListener("invalid", () => {
field.setAttribute("aria-invalid", "true");
});
});<p id="passwordHelp">Your password must be at least 10 characters long</p>
<input id="password" aria-describedby="passwordHelp passwordError" />
<p id="passwordError"></p>fields.forEach((field) => {
// ...
const feedback = document.createElement("p");
const id = field.id + "Error";
feedback.setAttribute("id", id);
// don't overwrite any existing aria-describedby
const prevIds = field.getAttribute("aria-describedBy");
const describedBy = prevIds ? prevIds + " " + id : id;
field.setAttribute("aria-describedBy", describedBy);
field.after(feedback);
// ...
});fields.forEach((field) => {
// ...
field.addEventListener("invalid", () => {
// ...
const message = field.validationMessage;
feedback.textContent = message;
});
});fields.forEach((field) => {
// ...
field.addEventListener("input", () => {
field.setAttribute("aria-invalid", "false");
feedback.textContent = "";
});
});fields.forEach((field) => {
// ...
field.addEventListener("blur", () => {
field.checkValidity();
});
});[aria-invalid="true"] {
border-color: red;
}
/*
* attr$="value" matches the _end_ of the attribute.
* e.g. this matches id="passwordError"
* but doesn't match id="passwordHelp".
* You could also just add a className ¯\_(ツ)_/¯
*/
[aria-invalid="true"] + [id$="Error"] {
color: red;
}
[aria-invalid="true"] + [id$="Error"]::before {
content: "⚠️ ";
}Learn how to use TypeScript to write more robust code
const title = document.createElement("h1");
title.className = "title";
title.textContent = "Hello world!";<h1 class="title">Hello world!</h1>const title = <h1 className="title">Hello world!</h1>;const title = _jsx("h1", { className: "title", children: "Hello world!" });
/*
* Over-simplified for examples sake:
{
type: "h1",
props: {
className: "title",
children: "Hello world!",
},
}
*/const title = <h1>Hello {5 * 5}</h1>;
// <h1>Hello 25</h1>const name = "oli";
const title = <h1>Hello {name}</h1>;
// <h1>Hello oli</h1>const number = Math.random();
const result = <div>{number > 0.5 ? "You won!" : "You lost"}</div>;
// 50% of the time: <div>You won!</div>
// the other 50%: <div>You lost</div>const number = 5 + 4 * 9;
const isEven = number % 2 === 0;
const message = isEven ? "It is even" : "It is odd";const message = if (isEven) { "It is even" } else { "It is odd" };
// this is not valid JS and will cause an errorfunction Title() {
return <h1 className="title">Hello world!</h1>;
}const fruits = ["apple", "orange", "banana"];
function FruitList() {
const items = fruits.map((fruit) => <li key={fruit}>{fruit}</li>);
return <ul>{items}</ul>;
}function Title() {
return <h1 className="title">Hello world!</h1>;
}
function Page() {
return (
<div className="page">
<Title />
</div>
);
}<Title name="oli" />
/**
* The above JSX is transformed into this:
* _jsx(Title, { name: "oli" });
*/function Title(props) {
console.log(props); // { name: "oli" }
return <h1 className="title">Hello world</h1>;
}function Title(props) {
return <h1 className="title">Hello {props.name}</h1>;
}function Page() {
return (
<div className="page">
<Title name="oli" />
<Title name="sam" />
</div>
);
}
/**
* <div class="page">
* <h1 class="title">Hello oli</h1>
* <h1 class="title">Hello sam</h1>
* </div>
*/function Title(props: { name: string }) {
return <h1 className="title">Hello {props.name}</h1>;
}type TitleProps = { name: string };
function Title(props: TitleProps) {
return <h1 className="title">Hello {props.name}</h1>;
}function Page() {
const fullname = "oliver" + " phillips";
return (
<div className="page">
<Title name={fullname} />
<Title name={String(5 * 5)} />
</div>
);
}
/**
* <div class="page">
* <h1 class="title">Hello oliver phillips</h1>
* <h1 class="title">Hello 25</h1>
* </div>
*/<Title>Hello oli</Title>
/**
* The above JSX is transformed into this:
* _jsx(Title, { children: "hello oli" });
*/function Title(props) {
return <h1 className="title">{props.children}</h1>;
}<Title>Hello oli</Title>
// <h1 class="title">Hello oli</h1>// pretend we have defined Image and BigText components above
<Title>
<Image src="hand-wave.svg" />
<BigText>Hello oli</BigText>
</Title>type TitleProps = { children: React.ReactNode };
function Title(props) {
return <h1 className="title">{props.children}</h1>;
}import ReactDOM from "react-dom/client";
function App() {
return (
<Page>
<Title>Hello world!</Title>
<p>Welcome to my page</p>
</Page>
);
}
const div = document.querySelector("#root")!; // The `!` tells TS it's definitely not null
const root = ReactDOM.createRoot(div);
root.render(<App />);function Alerter() {
return <button onClick={() => alert("hello!")}>Say hello</button>;
}function Counter() {
const count = 0;
return <button onClick={() => {}}>{count}</button>;
}import { useState } from "react";
function Counter() {
const stateArray = useState(0);
const count = stateArray[0];
const setCount = stateArray[1];
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}import { useState } from "react";
function Counter(props) {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<FancyButton count={count} setCount={setCount} />
<FancyText>{count}</FancyText>
</div>
);
}
function FancyButton(props) {
function increment() {
props.setCount(props.count + 1);
}
return (
<button className="fancy-button" value={props.count} onClick={increment}>
+ 1
</button>
);
}
function FancyText(props) {
return <p className="fancy-text">{props.children}</p>;
}const [count, setCount] = useState<number>(0);type Status = "loading" | "complete" | "error";
const [status, setStatus] = useState<Status>("loading");type FancyButtonProps = {
count: number;
setCount: React.Dispatch<React.SetStateAction<number>>;
};
function FancyButton(props: FancyButtonProps) {
function increment() {
props.setCount(props.count + 1);
}
return (
<button className="fancy-button" value={props.count} onClick={increment}>
+ 1
</button>
);
}// ...
const [count, setCount] = useState(0);
// ...
setInterval(() => {
setCount((previousCount) => {
const nextCount = previousCount + 1;
return nextCount;
});
}, 1000);
// or more concisely:
// setInterval(() => setCount(c => c + 1), 1000);function ChooseName() {
const [name, setName] = useState("");
function updateName(event) {
event.preventDefault();
setName(event.target.username.value);
}
return (
<form onSubmit={updateName}>
<input name="username" aria-label="Username" />
<button>Update name</button>
<output>Your name is: {username}</output>
</form>
);
}<button onClick={(event) => console.log(event)}>Click</button>function updateName(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
setName(event.target.username.value);
}function ChooseRating() {
const [rating, setRating] = useState(3);
function updateRating(event: React.ChangeEvent<HTMLInputElement>) {
setFruit(+event.target.value);
}
return (
<form>
<input
type="range"
value={rating}
onChange={updateRating}
min="1"
max="5"
aria-label="Rating"
/>
<output>{"⭐️".repeat(rating)}</output>
</form>
);
}function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
});
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);function KeyDisplay(props) {
const [key, setKey] = useState("");
useEffect(() => {
function updateKey(event: KeyboardEvent) {
setKey(event.key);
}
window.addEventListener("keydown", updateKey);
}, []);
return <div>{key}</div>;
}// ...
useEffect(() => {
function updateKey(event: KeyboardEvent) {
setKey(event.key);
}
window.addEventListener("keydown", updateKey);
return () => window.removeEventListener("keydown", updateKey);
}, []);
// ...function add(x: number, y: number) {
return x + y;
}mkdir ts-workshop && cd ts-workshopnpm init -ynpm i -D typescriptcode .const message: string = "It's working!";
console.log(message);node index.tsSyntaxError: Unexpected token ':'npx tsc index.tsvar message = "It's working!";
console.log(message);node index.jsnpx tsc --outDir build{
"compilerOptions": {
"outDir": "build",
"module": "NodeNext",
"strict": true
}
}npx tscnpx tsc --watchnode --watch build/index.js let index = 0;
function create(content) {
return {
content,
id: i++,
status: "incomplete",
createdAt: new Date(),
};
}Parameter 'content' implicitly has an 'any' type.function create(content: string) {
// ...
}let index: number = 0;function create(content: string): {
content: string;
id: number;
status: string;
createdAt: Date;
};let task = create("Learn TypeScript");
task.id; // number
task.x; // Property 'x' does not exist on type '{ content: string; id: number; status: string; createdAt: Date; }'.function create(content: string): {
content: string;
id: number;
status: string;
createdAt: Date;
} {
return {
content,
id: index++,
status: "incomplete",
createdAt: new Date(),
};
}function create(content: string): {
content: string;
id: number;
status: "incomplete";
createdAt: Date;
} {
// ...
}function complete(task) {
task.status = "complete";
}Parameter 'task' implicitly has an 'any' type.function complete(task: {
content: string;
id: number;
status: "incomplete";
createdAt: Date;
}) {
// ...
}type Task = {
content: string;
id: number;
status: "incomplete";
createdAt: Date;
};
function create(content: string): Task {
// ...
}
function complete(task: Task) {
// ...
}Type '"complete"' is not assignable to type '"incomplete"'.type Task = {
content: string;
id: number;
status: "incomplete" | "complete";
createdAt: Date;
};type Status = "incomplete" | "complete";
type Task = {
content: string;
id: number;
status: Status;
createdAt: Date;
};let tasks = [];
function create(content: string): Task {
let task = {
content,
id: index++,
status: "incomplete",
createdAt: new Date(),
};
tasks.push(task);
return task;
}Type '{ content: string; id: number; status: string; createdAt: Date; }' is not assignable to type 'Task'.
Types of property 'status' are incompatible.
Type 'string' is not assignable to type '"complete" | "incomplete"'.{
content: string;
id: number;
status: string;
createdAt: Date;
}function create(content: string): Task {
let task = {
// ...
status: "incomplete",
};
task.status = "aaaaaa";
tasks.push(task);
return task;
}function create(content: string): Task {
let task: Task = {
// ...
status: "incomplete",
};
tasks.push(task);
return task;
}function complete(id: number) {
let task = tasks.find((task) => task.id === id);
task.status = "complete";
}Variable 'tasks' implicitly has an 'any[]' type.Variable 'tasks' implicitly has type 'any[]' in some locations where its type cannot be determined.let tasks: Array<Task> = [];tasks.find((task) => task.id === id);'task' is possibly 'undefined'.function complete(id: number) {
let task = tasks.find((task) => task.id === id);
if (task) task.status = "complete";
}function remove(id: number) {
let index = todos.findIndex((t) => t.id === id);
todos.splice(index, 1);
}function remove(id: number) {
let index = tasks.findIndex((task) => task.id === id);
if (index !== -1) tasks.splice(index, 1);
}type Task = {
content: string;
id: number;
status: "incomplete" | "complete";
createdAt: Date;
birthday?: Date;
};type Task = {
content: string;
id: number;
status?: "incomplete" | "complete";
createdAt: Date;
birthday?: Date;
};function createBirthday(name: string, date: string): Task {
let task: Task = {
content: name,
id: index++,
createdAt: new Date(),
birthday: new Date(date),
};
tasks.push(task);
return task;
}function list() {
for (let task of tasks) {
let check = task.status === "complete" ? "[✔︎] " : "[ ] ";
let birthday = task.birthday.toLocaleDateString("en-GB");
console.log(check + task.content + " " + birthday);
}
}'task.birthday' is possibly 'undefined'.type Birthday = {
kind: "birthday";
name: string;
date: Date;
id: number;
createdAt: Date;
};let tasks: Array<Task | Birthday> = [];type Task = {
// ...
kind: "task";
status: "incomplete" | "complete";
};function create(content: string): Task {
let task: Task = {
kind: "task",
// ...
};
// ...
}
function createBirthday(name: string, date: string): Birthday {
let task: Birthday = {
kind: "birthday",
content,
date: new Date(birthday),
id: index++,
createdAt: new Date(),
};
tasks.push(task);
return task;
}
function complete(id: number) {
let task = tasks.find((task) => task.id === id);
if (task && task.kind === "task") task.status = "complete";
// Birthdays don't have `status`
}function list() {
for (let task of tasks) {
if (task.kind === "birthday") {
let birthday = task.date.toLocaleDateString("en-GB");
console.log("[★]" + task.name + " " + birthday);
} else {
let check = task.status === "complete" ? "[✔︎] " : "[ ] ";
console.log(check + task.content);
}
}
}import { styleText } from "node:util";Cannot find module 'node:util' or its corresponding type declarations.npm i -D @types/nodeconsole.log(styleText("dim", check) + styleText("bold", task.content));const el = document.querySelector("#test");
console.log(el.textContent); // 'el' is possibly 'null'.const el = document.querySelector("#test") as HTMLDivElement;
console.log(el.textContent);const el = document.querySelector("#test");
if (el instanceof HTMLDivElement) {
console.log(el.textContent);
}function stringify(value: unknown): string {
if (value === null || value === undefined) return "";
if (typeof value === "string") return value;
if (typeof value === "number") return value.toString();
if (Array.isArray(value)) return value.join(" ");
return "Unknown type";
}type Pet = {
name: string;
};
type Dog = Pet & {
says: "woof";
};
let fido: Dog = {
name: "fido",
says: "woof",
};type Result<Type> = Type | Error;
// Can either be the type we pass in, or an error
function run(): Result<number> {
// do some stuff that might not work
let result = calculationThatMightFail();
if (!result) {
return new Error("Failed");
} else {
return result;
}
}Learn how to use Node and Express to create and test HTTP servers
GET / HTTP/1.1
host: google.com
accept: text/htmlnpm install expresscode .HTTP/1.1 200 Ok
content-type: text/html
<!doctype html>
<html><body><h1>Welcome to Google</h1>...</body></html>mkdir node-server-introcd node-server-intronpm init -y// messages.js
const message1 = "hello";
const message2 = "goodbye";
module.exports = message1;// index.js
const whateverNameWeWant = require("./messages.js");
console.log(whateverNameWeWant); // Logs: "hello"
console.log(message2); // Error: message2 is not defined// messages.js
const message1 = "hello";
const message2 = "goodbye";
module.exports = {
message1: message1,
message2: message2,
};// index.js
const messages = require("./messages.js");
// or using destructuring:
// const { message1, message2 } = require("./messages.js");
console.log(messages.message1); // Logs: "hello"
console.log(messages.message2); // Logs: "goodbye"const fs = require("node:fs");
fs.readFile("my-file.txt");const express = require("express");const express = require("express");
const server = express();node server.js// server.js
const express = require("express");
const server = express();
module.exports = server;// index.js
const server = require("./server.js");
server.listen(3000);node index.jsserver.listen(3000, () => console.log("Listening at http://localhost:3000"));curl localhost:3000// server.js
server.get("/", (request, response) => {
response.send("hello");
});const test = require("node:test");
const assert = require("node:assert");
test("the test works", () => {
assert.equal(1, 1);
});node test/server.test.jsnode --testconst test = require("node:test");
const assert = require("node:assert");
test("home route returns expected page", async () => {
// test goes here
});const test = require("node:test");
const assert = require("node:assert");
const server = require("../server.js");
test("home route returns expected page", async () => {
const app = server.listen(9876);
});test("home route returns expected page", async () => {
const app = server.listen(9876);
const response = await fetch("http://localhost:9876");
app.close();
assert.equal(response.status, 200);
});test("home route returns expected page", async () => {
const app = server.listen(9876);
const response = await fetch("http://localhost:9876");
app.close();
assert.equal(response.status, 200);
const body = await response.text();
assert.equal(body, "hello");
});TEST=123 node index.jsSET TEST=123 & node index.jsconsole.log(process.env.TEST); // Logs: 123// index.js
const server = require("./server.js");
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => console.log(`Listening at http://localhost:${PORT}`));PORT=8080 node index.jsserver.get("/uh-oh", (request, response) => {
response.status(500);
response.send("something went wrong");
});response.status(500).send("something went wrong");response.set("x-fake-header", "my-value");response.set({
"x-fake-header": "my value",
"x-another-header": "another value",
});server.get("/", (request, response) => {
response.send(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Home</title>
</head>
<body>
<h1>Hello</h1>
</body>
</html>
`);
});server.get("/", (request, response) => {
const year = new Date().getFullYear();
response.send(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Home</title>
</head>
<body>
<h1>Hello, it's ${year}</h1>
</body>
</html>
`);
});<form action="/search" method="GET">
<input name="keyword" />
</form>// tests/server.test.js
test("/search returns message including keyword", async () => {
const app = server.listen(9876);
const response = await fetch("http://localhost:9876/search?keyword=bananas");
app.close();
assert.equal(response.status, 200);
const body = await response.text();
assert.match(body, /You searched for bananas/);
});// server.js
server.get("/search", (request, response) => {
const keyword = request.query.keyword;
response.send(`<p>You searched for ${keyword}</p>`);
}server.get("/users/:name", (request, response) => {
const name = request.params.name;
response.send(`<h1>Hello ${name}</h1>`);
});server.use((request, response) => {
response.status(404).send("<h1>Not found</h1>");
});server.get("/", (request, response, next) => {
console.log(request.method + " " + request.url);
next();
});
server.get("/", (request, response) => {
response.send(`...`);
});function logger(request, response, next) {
console.log(request.method + " " + request.url);
next();
}
server.get("/", logger, (request, response) => {
response.send("<h1>Hello</h1>");
});server.use(logger);/* public/style.css */
body {
color: red;
}// server.js
const staticHandler = express.static("public");
server.use(staticHandler);server.get("/", (request, response) => {
response.send(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Home</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>Hello</h1>
</body>
</html>
`);
});server.post("/submit", (request, response) => {
response.send("thanks for submitting");
});// tests/server.test.js
test("/submit route responds to POST requests", async () => {
const app = server.listen(9876);
const response = await fetch("http://localhost:9876/submit", {
method: "POST",
});
app.close();
assert.equal(response.status, 200);
const body = await response.text();
assert.match(body, /thanks for submitting/);
});// server.js
const bodyParser = express.urlencoded();
server.post("/submit", bodyParser, (request, response) => {
const name = request.body.name;
response.send(`thanks for submitting, ${name}`);
});// tests/server.test.js
test("/submit route responds to POST requests", async () => {
const app = server.listen(9876);
const response = await fetch("http://localhost:9876/submit", {
method: "POST",
body: "name=oli",
headers: {
"content-type": "application/x-www-form-urlencoded",
},
});
app.close();
assert.equal(response.status, 200);
const body = await response.text();
assert.match(body, /thanks for submitting, oli/);
}// server.js
const bodyParser = express.urlencoded();
server.post("/submit", bodyParser, (request, response) => {
const name = request.body.name;
response.redirect(`/submit/success?name=${name}`);
});// server.js
server.get("/submit/success", (request, response) => {
const name = request.query.name;
response.send(`<p>thanks for submitting, ${name}</p>`);
});