我正在查看有关将 Google Cloud Run 中的 Next.JS 应用程序连接到 PostgreSQL 数据库的文档 https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/main/cloud-sql/postgres/knex /README.md.
我已完成以下操作:
我不确定如何使用连接到数据库,因为我在 Cloud Run 中收到以下错误:
⨯ Error: connect ECONNREFUSED 127.0.0.1:5432
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1607:16)
at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 5432
}
我的应用程序包含一个用户可以注册的页面。我尝试使其尽可能简单,以便在扩展数据库之前修复与数据库的连接。
注册页面
sql/1u.sql
create extension if not exists citext;
create table if not exists public.users (
id bigserial primary key,
username citext unique not null,
password text,
created_at timestamp default now(),
updated_at timestamp default now()
);
app/(公共)/signup/page.tsx
import Form from "./form";
export default async function SignUp() {
return (
<div>
<Form />
</div>
);
}
app/(公共)/signup/form.tsx
"use client";
import React, { FormEvent, useState } from "react";
function Form() {
const [username, setUsername] = useState<undefined | string>("");
const [password, setPassword] = useState<undefined | string>("");
const [confirmPassword, setConfirmPassword] = useState<undefined | string>(
""
);
const [errors, setErrors] = useState<string[]>([]);
async function handleSubmit(e: FormEvent) {
e.preventDefault();
setErrors([]);
if (password != confirmPassword) {
const newErrors = [];
newErrors.push("Passwords do not match.");
setErrors(newErrors);
return;
}
const res = await fetch("/api/signup", {
method: "POST",
body: JSON.stringify({ username, password }),
});
if (res.ok) {
window.location.href = "/signin";
}
else {
alert("sign up failed");
}
}
return (
<form onSubmit={handleSubmit} className="flex flex-col gap-2 p-5 max-w-xs w-full bg-slate-800 rounded-lg">
<div className="text-center">
<h3 className="font-semibold">Sign Up</h3>
</div>
<div className="my-3">
<hr />
</div>
<div>
<div className="flex flex-col gap-2">
<label>Username</label>
<input className="text-black p-3 border border-slate-700 rounded-lg" type="text" onChange={(e) => setUsername(e.target.value)} value={username} id="username" placeholder="Username" required />
</div>
</div>
<div className="flex flex-col gap-2 mt-4">
<label>Password</label>
<input className="text-black p-3 border border-slate-700 rounded-lg" type="password" onChange={(e) => setPassword(e.target.value)} value={password} id="password" placeholder="Password" required />
</div>
<div className="flex flex-col gap-2 mt-4">
<label>Confirm Password</label>
<input className="text-black p-3 border border-slate-700 rounded-lg" type="password" onChange={(e) => setConfirmPassword(e.target.value)} value={confirmPassword} id="confirm-password" placeholder="Confirm Password" required />
</div>
<button type="submit" className="mt-4 bg-slate-900 text-white p-3 rounded-lg">Sign Up</button>
{errors.map((error) => {
return (
<div key={error} className="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400 mt-4" role="alert">
<span className="font-medium">{error}</span>
</div>
);
})}
</form>
);
}
export default Form;
应用程序/api/signup/route.tsx
import { sql } from "@/db";
import bcrypt from "bcrypt";
import { NextResponse } from "next/server";
export async function POST(request: Request){
const json = await request.json();
// Does the username exists?
const res = await sql(
"SELECT id, username FROM users WHERE username ILIKE $1",
[json.username]
);
if (res.rowCount! > 0) {
return NextResponse.json({ error: "user already exists" }, { status: 400 });
}
// Genereate user
const saltRounds = 10;
const hash = await bcrypt.hash(json.password, saltRounds);
await sql("INSERT INTO users (username, password) VALUES ($1, $2)", [
json.username,
hash,
]);
return NextResponse.json({ msg: "registration success" }, { status: 201 });
}
db.ts
import { Client, QueryResult } from "pg";
import { loadEnvConfig } from "@next/env";
const projectDir = process.cwd();
loadEnvConfig(projectDir)
// Generate a Postgres client
export async function getClient(): Promise<Client>{
// Client with URL
if (process.env.DB_URL) {
const client = new Client({
connectionString: process.env.DB_URL + "?sslmode=require",
});
return client;
}
// Client with username, host, database, password
const client = new Client({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASS,
port: parseInt(process.env.DB_PORT!)
});
return client;
}
// Handle connection, SQL and end the connection
export async function sql(sql: string, values?: Array<any>): Promise<QueryResult<any>> {
const client = await getClient();
await client.connect();
const res = await client.query(sql, values);
await client.end();
return res;
}
我想我需要将 db.ts 从 Google 示例更改为 connect-unix.js,但是我对如何执行此操作有点迷失..
这可能是一种可能的场景和/或解决方案。
PostgreSQL 标准要求套接字路径中包含 .s.PGSQL.5432 后缀,如官方公共文档此处中所述。有些库自动应用此后缀,但其他库则要求您指定套接字路径,如下所示:
/cloudsql/INSTANCE_CONNECTION_NAME/.s.PGSQL.5432
您的情况似乎缺少:
INSTANCE_UNIX_SOCKET=/cloudsql/my-project:europe-north1:my-db
因此,为您的 Cloud SQL 实例 unix 套接字路径添加后缀 .s.PGSQL.5432 可能会解决您的问题。
INSTANCE_UNIX_SOCKET=/cloudsql/my-project:europe-north1:my-db/.s.PGSQL.5432