Delete data from database using row action

The “admin_action_{$_REQUEST[‘action’]}” hook does not pass any arguments. You would need to get the article_id from the $_REQUEST array. There are also a few other concerns such as:

  1. You’re not assigning an action when you call wp_create_nonce().
  2. As Mafnah suggets in their ansewr, you should use $wpdb::delete() as a shortcut for using $wpdb::prepare().

Here’s how you could rewrite this:

function column_name( $item ) {
    $delete_nonce = wp_create_nonce( 'delete_article' );
    $actions = [
        'delete' => add_query_arg( array(
            'action'        => 'delete',
            'article_id'    => $item['article_id'],
            '_wpnonce'      => $delete_nonce,
        ), admin_url( 'admin.php' ) ),
    ];
    /* Etc... */
}

function delete_article() {

    global $wpdb;

    // Ensure we can verify our nonce.
    if( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'delete_article' ) ) {
        wp_die( esc_html__( 'Could not verify request. Please try again.' ) );

    // Ensure we have an article ID provided.
    } else if( ! isset( $_REQUEST['article_id'] ) ) {
        wp_die( esc_html__( 'Could not find provided article ID. Please try again.' ) );

    // Ensure we're able to delete the actual article.
    } else if( false === $wpdb->delete( 'wp_copywriter_articles', array( 'article_id' => $_REQUEST['article_id'] ) ) ) {
        wp_die( esc_html__( 'Could not delete the provided article. Please try again.' ) );
    }

    // Redirect back to original page with 'success' $_GET
    wp_safe_redirect( add_query_arg( array(
        'page' => ( isset( $_REQUEST['page'] ) ) ? $_REQUEST['page'] : '404',
        'success' => 1,
    ), admin_url( 'admin.php' ) ) );
    exit();

}