Skip to main content

Command Palette

Search for a command to run...

detail

Let's get into the nitty-gritty details of setting up your Django REST Framework (DRF) backend and integrating it with your SvelteKit frontend. We'll go step-by-step, focusing on clear instructions.

Assumptions:

  • You have a Django project named myapi set up with apps auth and content.

  • You have a SvelteKit project ready to go.

  • You have Python, Node.js, and npm (or yarn/pnpm) installed.

  • You have a GitHub OAuth application configured with a Client ID and Secret.

  • You have PostgreSQL installed and set up (or know how to use Docker for it).

Let's Start with the Backend (Django/DRF):

1. PostgreSQL Database Configuration (if not using Docker):

  • Ensure PostgreSQL is running.

  • Create a database (e.g., my_app_db).

  • Create a user and grant them permissions on the database.

  • In your myapi/settings.py file, update the DATABASES section with your credentials:

      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.postgresql',
              'NAME': 'my_app_db',
              'USER': 'your_db_user',
              'PASSWORD': 'your_db_password',
              'HOST': 'localhost',
              'PORT': '5432',
          }
      }
    

2. Django App: auth (Authentication):

  • auth/models.py (You don't need to create a new user model for Github Auth):

      # Using Django's default User model (no changes needed)
    
  • auth/serializers.py:

      from rest_framework import serializers
      from django.contrib.auth.models import User
    
      class UserSerializer(serializers.ModelSerializer):
          class Meta:
              model = User
              fields = ['id', 'username', 'email']
    
      class AuthSerializer(serializers.Serializer):
          code = serializers.CharField(required=True)
    
  • auth/views.py:

    ```python from rest_framework import status from rest_framework.views import APIView from rest_framework.response import Response from django.shortcuts import redirect from django.conf import settings from django.contrib.auth.models import User from .serializers import UserSerializer, AuthSerializer from social_django.utils import load_strategy, load_backend from social_core.exceptions import SocialAuthBaseException import jwt from rest_framework_simplejwt.tokens import RefreshToken

    def get_tokens_for_user(user): refresh = RefreshToken.for_user(user)

    return { 'refresh': str(refresh), 'access': str(refresh.access_token), }

    class LoginView(APIView): def get(self, request): strategy = load_strategy() backend = load_backend(strategy, 'github', redirect_uri=settings.GITHUB_CALLBACK_URL) auth_url = backend.auth_url() return redirect(auth_url)

    class CallbackView(APIView): def post(self, request): serializer = AuthSerializer(data=request.data) if serializer.is_valid(raise_exception=True): strategy = load_strategy() backend = load_backend(strategy, 'github', redirect_uri=settings.GITHUB_CALLBACK_URL) try: user = backend.complete(request, serializer.validated_data.get('code')) except SocialAuthBaseException as e: return Response({'error': 'Social Authentication failed.'}, status=status.HTTP_400_BAD_REQUEST) if user: if not isinstance(user, User): user = user.user tokens = get_tokens_for_user(user) serializer = UserSerializer(user) return Response({'user': serializer.data, 'tokens': tokens}, status=status.HTTP_200_OK) else: return Response({'error': 'Social Authentication failed'}, status=status.HTTP_400_BAD_REQUEST)

class LogoutView(APIView): def post(self, request): return Response({'message': 'Logged Out'}, status=status.HTTP_200_OK)

class UserInfoView(APIView): def get(self, request): user = request.user serializer = UserSerializer(user) return Response(serializer.data, status=status.HTTP_200_OK)


* `auth/`[`urls.py`](http://urls.py):

    ```python
    from django.urls import path
    from .views import LoginView, CallbackView, LogoutView, UserInfoView

    urlpatterns = [
        path('login/', LoginView.as_view(), name='login'),
        path('callback/', CallbackView.as_view(), name='callback'),
        path('logout/', LogoutView.as_view(), name='logout'),
        path('user/', UserInfoView.as_view(), name='user'),
    ]
  • myapi/urls.py:

      from django.contrib import admin
      from django.urls import path, include
    
      urlpatterns = [
          path('admin/', admin.site.urls),
          path('auth/', include('auth.urls')),
          path('content/', include('content.urls')),
      ]
    
  • myapi/settings.py (Crucial Configurations):

      INSTALLED_APPS = [
         ...,
          'rest_framework',
          'rest_framework_simplejwt',
          'social_django',
         ...
      ]
    
      AUTHENTICATION_BACKENDS = (
          'social_core.backends.github.GithubOAuth2',
          'django.contrib.auth.backends.ModelBackend',
      )
    
      REST_FRAMEWORK = {
          'DEFAULT_AUTHENTICATION_CLASSES': (
              'rest_framework_simplejwt.authentication.JWTAuthentication',
          )
      }
    
      GITHUB_CALLBACK_URL='http://localhost:8000/auth/callback/'
      SOCIAL_AUTH_GITHUB_KEY = os.environ.get('GITHUB_CLIENT_ID')
      SOCIAL_AUTH_GITHUB_SECRET = os.environ.get('GITHUB_CLIENT_SECRET')
    
    • Environment Variables: Make sure you set up environment variables in your terminal or .env file
    export GITHUB_CLIENT_ID="your_client_id"
    export GITHUB_CLIENT_SECRET="your_client_secret"

3. Django App: content (Example Data):

  • content/models.py:

      from django.db import models
      from django.contrib.auth.models import User
    
      class BlogPost(models.Model):
          title = models.CharField(max_length=200)
          content = models.TextField()
          author = models.ForeignKey(User, on_delete=models.CASCADE)
          created_at = models.DateTimeField(auto_now_add=True)
    
  • content/serializers.py:

      from rest_framework import serializers
      from .models import BlogPost
    
      class BlogPostSerializer(serializers.ModelSerializer):
          author = serializers.ReadOnlyField(source='author.username')
          class Meta:
              model = BlogPost
              fields = ['id', 'title', 'content', 'author', 'created_at']
    
  • content/views.py:

    ```python from rest_framework import generics from .models import BlogPost from .serializers import BlogPostSerializer from rest_framework.permissions import IsAuthenticated

    class BlogPostListCreateView(generics.ListCreateAPIView): queryset = BlogPost.objects.all() serializer_class = BlogPostSerializer permission_classes = [IsAuthenticated]

    def perform_create(self, serializer): serializer.save(author=self.request.user)

class BlogPostRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView): queryset = BlogPost.objects.all() serializer_class = BlogPostSerializer permission_classes = [IsAuthenticated]


* `content/`[`urls.py`](http://urls.py):

    ```python
    from django.urls import path
    from .views import BlogPostListCreateView, BlogPostRetrieveUpdateDestroyView

    urlpatterns = [
        path('', BlogPostListCreateView.as_view(), name='blogpost-list-create'),
        path('<int:pk>/', BlogPostRetrieveUpdateDestroyView.as_view(), name='blogpost-retrieve-update-destroy'),
    ]

4. Run Migrations:

  • Make migrations for both auth and content apps:

      python manage.py makemigrations auth content
      python manage.py migrate
    

5. Test the API:

  • Run the Django development server (python manage.py runserver).

  • Use tools like Postman, curl, or a browser to test your API endpoints.

    • Try accessing http://localhost:8000/auth/login/. You should be redirected to GitHub.

    • After you log in with github you are gonna be redirected back to http://localhost:8000/auth/callback/

      • NOTE: The frontend will send the github code in the body to the backend endpoint
    • Try hitting http://localhost:8000/content/ with a POST request with a title and content in the body.

      • Note: This endpoint should require authentication, meaning you would need to send a JWT token in the headers of the request. Authorization: Bearer <token>