Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch naive database implementation to PostgreSQL #4

Merged
merged 76 commits into from
Oct 24, 2024
Merged

Conversation

ProchaLu
Copy link
Member

@ProchaLu ProchaLu commented Jul 2, 2024

Closes #10

For the Expo React Native Guest List Example, we used a global array to save the guests. This PR updates the example and now uses PostgreSQL with Postgres.js and Ley.

TODO

app/[id]+api.ts Outdated
Copy link
Member

@karlhorky karlhorky Jul 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. as @ProchaLu mentioned, let's do another PR (stacked on top of this one) to move these to app/api/ (let's keep the files in place for this PR, to keep the diff simple)
  2. deployment
    1. where are we suggesting students deploy Expo API Routes + PostgreSQL to?
    2. let's add a section to the React Native / Expo lecture notes about deployment (let's track that PR / issue also here)
    3. let's do a video of this and integrate it with the current material

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. created a PR for the folder structure change to app/api Restructure to use /api folder with guestId  #6

Copy link
Member Author

@ProchaLu ProchaLu Jul 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. i. We suggest to deploy the Expo API Routes with PostgreSQL on Vercel.
    ii. Created a PR for the Deployment section on the Learning Platform and will create a PR for the Lecture notes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. as @ProchaLu mentioned, let's add a section to the React Native / Expo lecture notes (let's track that PR / issue also here)
  2. let's do a video of this and integrate it with the current material

Copy link
Member Author

@ProchaLu ProchaLu Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Created a PR for the React Native PostgreSQL lecture notes https://github.com/upleveled/courses/pull/2546
  2. Created a small video

Comment on lines 12 to 35
function connectOneTimeToDatabase() {
if (!('postgresSqlClient' in globalThis)) {
globalThis.postgresSqlClient = postgres({
transform: {
...postgres.camel,
undefined: null,
},
});
}

// Use Next.js Dynamic Rendering in all database queries:
//
// Wrap sql`` tagged template function to call `noStore()` from
// next/cache before each database query. `noStore()` is a
// Next.js Dynamic Function, which causes the page to use
// Dynamic Rendering
//
// https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic-rendering
return ((
...sqlParameters: Parameters<typeof globalThis.postgresSqlClient>
) => {
return globalThis.postgresSqlClient(...sqlParameters);
}) as typeof globalThis.postgresSqlClient;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we don't need this noStore in expo, we may instead connect to the database like in the GraphQL example which is simpler

https://github.com/upleveled/graphql-example-spring-2024-atvie/blob/65f8a40fe857796e7cd649eb3ca5e4c8f2db3556/database/connect.ts#L16-L24

function connectOneTimeToDatabase() {
  if (!globalThis.postgresSqlClient) {
    globalThis.postgresSqlClient = postgres(postgresConfig);
  }
  return globalThis.postgresSqlClient;
}

// Connect to PostgreSQL
export const sql = connectOneTimeToDatabase();

Copy link
Member

@karlhorky karlhorky Jul 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@upleveled upleveled deleted a comment from ProchaLu Jul 3, 2024
ProchaLu and others added 3 commits July 4, 2024 12:46
Co-authored-by: Karl Horky <karl.horky@gmail.com>
Co-authored-by: Karl Horky <karl.horky@gmail.com>
Co-authored-by: Karl Horky <karl.horky@gmail.com>
export function GET(request: Request): Response {
export async function GET(request: Request): Promise<Response> {
Copy link
Member

@karlhorky karlhorky Jul 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no way to type the response data of the Expo API response is there?

eg. like NextResponse in Next.js?

if not, we should probably:

  1. see if it's possible to copy/paste a minimal implementation (eg. this TypeScript Playground version) to the Expo example repo (add to the Expo setup steps) - name could be something like ExpoApiResponse
  2. open an Expo issue, if one doesn't already exist

I also created an issue to enforce these type checks in the ESLint config (once we have them):

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic type parameters for Expo API Routes Issue

Copy link
Member Author

@ProchaLu ProchaLu Oct 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the types for the ExpoApiResponse in this commit
98e83ec

In the previous commit, I added types, that I already had in my migrations file. Removed again
23be733

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import { Guest } from '../migrations/00000-createTableGuests';
import { sql } from './connect';

export const getGuests = async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const ... = async () => arrow function style used here to be closer to the cache() database query function code we have in the Next.js example:

https://github.com/upleveled/next-js-example-spring-2024-atvie/blob/869d615738ee991fd80f31b44dfa3c4f79a7ce3d/database/animals.ts#L50-L62

@upleveled upleveled deleted a comment from ProchaLu Oct 24, 2024
@upleveled upleveled deleted a comment from Eprince-hub Oct 24, 2024
@upleveled upleveled deleted a comment from ProchaLu Oct 24, 2024
@upleveled upleveled deleted a comment from ProchaLu Oct 24, 2024
@upleveled upleveled deleted a comment from ProchaLu Oct 24, 2024
@upleveled upleveled deleted a comment from ProchaLu Oct 24, 2024
ProchaLu and others added 2 commits October 24, 2024 22:17
Co-authored-by: Karl Horky <karl.horky@gmail.com>
@upleveled upleveled deleted a comment from ProchaLu Oct 24, 2024
@upleveled upleveled deleted a comment from ProchaLu Oct 24, 2024
@upleveled upleveled deleted a comment from ProchaLu Oct 24, 2024

useFocusEffect(
useCallback(() => {
const getGuests = async () => {
Copy link
Member

@karlhorky karlhorky Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async function getGuests instead of arrow function

(for all functions inside useCallback following this pattern)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to functions
70e748d

Comment on lines 35 to 49
try {
const response = await fetch('/api/guests', {
headers: {
Cookie: 'name=value',
},
});
const body: { guests: Guest[] } = await response.json();

setGuests(body.guests);
} catch (error) {
console.error('Error fetching guests', error);
}
};

getGuests().catch(() => {});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • no need for the try/catch - you have the .catch() down below
  • inside of the .catch() is where the console.error should be

(for all functions inside useCallback following this pattern)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catch the errors now
70e748d

Comment on lines 109 to 114
try {
const body: { error: string } = await response.json();
if (body.error) {
errorMessage = body.error;
}
} catch {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another try/catch that is swallowing errors - we should avoid this pattern

we want it to throw if there are errors

(check for all try/catch patterns to see if we need them)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed some try/catch and catch errors
70e748d

Comment on lines 112 to 115
const [edit, setEdit] = useState<boolean>(false);
const [firstName, setFirstName] = useState<string>('');
const [lastName, setLastName] = useState<string>('');
const [attending, setAttending] = useState<boolean>(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these all don't need generic args

Suggested change
const [edit, setEdit] = useState<boolean>(false);
const [firstName, setFirstName] = useState<string>('');
const [lastName, setLastName] = useState<string>('');
const [attending, setAttending] = useState<boolean>(false);
const [edit, setEdit] = useState(false);
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [attending, setAttending] = useState(false);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in this commit
02412d3

Comment on lines 61 to 65
export const deleteGuestInsecure = async (id: number) => {
const [guest] = await sql<Guest[]>`
DELETE FROM guests
WHERE
id = ${id}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const deleteGuestInsecure = async (id: number) => {
const [guest] = await sql<Guest[]>`
DELETE FROM guests
WHERE
id = ${id}
export const deleteGuestInsecure = async (guestId: Guest['id']) => {
const [guest] = await sql<Guest[]>`
DELETE FROM guests
WHERE
id = ${guestId}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in this commit
02412d3

export default function GuestPage() {
const { guestId } = useLocalSearchParams();

const [edit, setEdit] = useState<boolean>(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since it's a boolean, let's switch to the is prefix convention:

isEditing, setIsEditing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change variable 02412d3

@upleveled upleveled deleted a comment from ProchaLu Oct 24, 2024
@ProchaLu ProchaLu merged commit 8bf88cf into next Oct 24, 2024
1 check passed
@ProchaLu ProchaLu deleted the add-postgres branch October 24, 2024 22:17
Copy link
Member

@karlhorky karlhorky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants